yugapurush commited on
Commit
368f410
·
1 Parent(s): a0a8f15
Files changed (1) hide show
  1. app.py +233 -0
app.py ADDED
@@ -0,0 +1,233 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import requests
3
+ import json
4
+ import asyncio
5
+ import logging
6
+ from typing import Dict, List, Any, Optional
7
+
8
+ # Set up logging to help debug issues
9
+ logging.basicConfig(level=logging.INFO)
10
+ logger = logging.getLogger(__name__)
11
+
12
+ class DolibarrAPI:
13
+ base_url = "http://localhost/dolibarr/api/index.php"
14
+
15
+ def __init__(self, api_key: str):
16
+ """
17
+ Just a constructor, sets up the api key and some other stuff
18
+
19
+ Args:
20
+ api_key (str): Dolibarr user api key
21
+ """
22
+ self.api_key = "OKgV53jdbT1p1tKuZrB05eK9z0p9I2YX"
23
+ self.headers = {
24
+ 'DOLAPIKEY': api_key,
25
+ 'Content-Type': 'application/json',
26
+ 'Accept': 'application/json'
27
+ }
28
+
29
+ def _request(self, method: str, endpoint: str, data: Optional[dict] = None, params: Optional[dict] = None) -> Any:
30
+ """
31
+ basic method used for making http request to Dolibarr api
32
+
33
+ Args:
34
+ method (str): The type of HTTP - GET, POST, PUT, DELETE,etc
35
+ endpoint (str): api endpoint based on the request type - "/invoice" , "/thirdparties",etc
36
+ data (Optional[dict]) : data to be sent in request body, dictionary
37
+ params (Optional[dict]) : dictionary of params to be sent with this request (for GET)
38
+ """
39
+
40
+ base_url = "http://localhost/dolibarr/api/index.php"
41
+ url = f"{base_url}{endpoint}"
42
+
43
+ try:
44
+ response = requests.request(method, url, headers=self.headers, json=data, params=params)
45
+ response.raise_for_status()
46
+ return response.json()
47
+ except requests.exceptions.RequestException as e:
48
+ logger.error(f"API request failed: {e}")
49
+ return {"error": f"API request failed: {str(e)}"}
50
+ except json.JSONDecodeError as e:
51
+ logger.error(f"JSON decode error: {e}")
52
+ return {"error": f"Invalid JSON response: {str(e)}"}
53
+
54
+ # --- GET REQUESTS ------
55
+ def get_req(self, endpoint: str, params: Optional[dict] = None):
56
+ return self._request('GET', endpoint, params=params)
57
+
58
+ # --- POST requests -----
59
+ def post_req(self, endpoint: str, params: dict):
60
+ return self._request("POST", endpoint, data=params)
61
+
62
+ # --- PUT requests ----
63
+ def put_req(self, endpoint: str, params: dict):
64
+ return self._request("PUT", endpoint, data=params)
65
+
66
+ # --- DELETE requests ----
67
+ def del_req(self, endpoint: str, params: Optional[dict] = None):
68
+ return self._request("DELETE", endpoint, params=params)
69
+
70
+ def dolibarr_interface(method: str, endpoint: str, api_key="OKgV53jdbT1p1tKuZrB05eK9z0p9I2YX", payload_str: str = "") -> str:
71
+ """
72
+ To orchestrate the API call from start to finish based on simple string and dictionary inputs
73
+
74
+ Args:
75
+ method (str): http method type ( GET, POST, PUT, DELETE,etc)
76
+ endpoint (str): api endpoint, basically which api to call (/invoices, /thirdparties,/products,etc)
77
+ api_key : dolibarr api key
78
+ payload_str (str): payload as JSON string to send as request body
79
+ """
80
+
81
+ try:
82
+ api = DolibarrAPI(api_key)
83
+ method = method.upper()
84
+
85
+ # Parse payload if provided
86
+ payload = None
87
+ if payload_str and payload_str.strip():
88
+ try:
89
+ payload = json.loads(payload_str)
90
+ except json.JSONDecodeError as e:
91
+ return json.dumps({"error": f"Invalid JSON payload: {str(e)}"}, indent=2)
92
+
93
+ if method == 'GET':
94
+ result = api.get_req(endpoint, payload)
95
+ elif method == 'POST':
96
+ if not payload:
97
+ return json.dumps({"error": "POST requests require a payload"}, indent=2)
98
+ result = api.post_req(endpoint, payload)
99
+ elif method == 'PUT':
100
+ if not payload:
101
+ return json.dumps({"error": "PUT requests require a payload"}, indent=2)
102
+ result = api.put_req(endpoint, payload)
103
+ elif method == 'DELETE':
104
+ result = api.del_req(endpoint, payload)
105
+ else:
106
+ return json.dumps({"error": f"Invalid HTTP method '{method}' selected."}, indent=2)
107
+
108
+ cleaned = clean_json_response(result)
109
+ return json.dumps(cleaned, indent=2)
110
+
111
+ except Exception as e:
112
+ logger.error(f"Unexpected error in dolibarr_interface: {e}")
113
+ return json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2)
114
+
115
+ def clean_json_response(data: Any) -> Any:
116
+ """
117
+ Recursively clean JSON response by removing null values, empty strings, and empty collections.
118
+
119
+ Args:
120
+ data: The data to clean (can be dict, list, or primitive type)
121
+
122
+ Returns:
123
+ Cleaned data with null values and empty collections removed
124
+ """
125
+ if isinstance(data, dict):
126
+ return {
127
+ k: clean_json_response(v)
128
+ for k, v in data.items()
129
+ if v is not None and v != "" and clean_json_response(v) is not None
130
+ }
131
+ elif isinstance(data, list):
132
+ cleaned = [clean_json_response(item) for item in data]
133
+ return [item for item in cleaned if item is not None and item != ""]
134
+ else:
135
+ return data
136
+
137
+ # Create the Gradio interface with better error handling
138
+ def create_interface():
139
+ """Create and return the Gradio interface"""
140
+
141
+ demo = gr.Interface(
142
+ fn=dolibarr_interface,
143
+ inputs=[
144
+ gr.Dropdown(
145
+ choices=["GET", "POST", "PUT", "DELETE"],
146
+ label="HTTP Method",
147
+ value="GET"
148
+ ),
149
+ gr.Dropdown(
150
+ choices=["/thirdparties", "/invoices", "/products", "/contacts", "/users"],
151
+ label="API Endpoint",
152
+ value="/thirdparties",
153
+ allow_custom_value=True
154
+ ),
155
+ gr.Textbox(
156
+ label="API Key",
157
+ value="OKgV53jdbT1p1tKuZrB05eK9z0p9I2YX"
158
+ ),
159
+ gr.Textbox(
160
+ label="Payload (JSON format)",
161
+ placeholder='{"ref": "PROD-001", "label": "Product Name", "price": "99.99"}'
162
+ )
163
+ ],
164
+ outputs=gr.Textbox(
165
+ label="API Response",
166
+ #lines=10
167
+ ),
168
+ title="Dolibarr AI Agent/Personal ERP Assistant",
169
+ description="Interact with your Dolibarr ERP system through API calls. Select method, endpoint, and provide JSON payload for POST/PUT requests.",
170
+ examples=[
171
+ ["GET", "/thirdparties", "OKgV53jdbT1p1tKuZrB05eK9z0p9I2YX", ""],
172
+ ["POST", "/products", "OKgV53jdbT1p1tKuZrB05eK9z0p9I2YX", '{"ref": "PROD-007", "label": "New AI-Powered Gadget", "price": "199.99", "tva_tx": "20.0"}']
173
+ ]
174
+ )
175
+
176
+ return demo
177
+
178
+ # Main execution with better MCP server handling
179
+ if __name__ == '__main__':
180
+ try:
181
+ demo = create_interface()
182
+
183
+ # Launch with MCP server but with better error handling
184
+ logger.info("Starting Gradio application with MCP server...")
185
+
186
+ # Try launching with MCP server first
187
+ try:
188
+ demo.launch(
189
+ mcp_server=True,
190
+ server_name="127.0.0.1",
191
+ server_port=7860,
192
+ share=False,
193
+ debug=True,
194
+ show_error=True
195
+ )
196
+ except Exception as mcp_error:
197
+ logger.error(f"Failed to start with MCP server: {mcp_error}")
198
+ logger.info("Falling back to regular Gradio interface...")
199
+
200
+ # Fallback to regular Gradio interface
201
+ demo.launch(
202
+ server_name="127.0.0.1",
203
+ server_port=7861, # Use different port
204
+ share=False,
205
+ debug=True,
206
+ show_error=True
207
+ )
208
+
209
+ except Exception as e:
210
+ logger.error(f"Failed to start application: {e}")
211
+ print(f"Error starting application: {e}")
212
+
213
+ #----- Example usage ----
214
+ # Uncomment these lines to test the API functionality directly
215
+
216
+ # # Test basic API calling
217
+ # print("Testing API connection...")
218
+ # list_result = dolibarr_interface(method='GET', endpoint='/thirdparties')
219
+ # print("Thirdparties list:", list_result)
220
+
221
+ # # Test creating a product
222
+ # new_product_data = {
223
+ # "ref": "PROD-007",
224
+ # "label": "New AI-Powered Gadget",
225
+ # "price": "199.99",
226
+ # "tva_tx": "20.0"
227
+ # }
228
+ # create_result = dolibarr_interface(
229
+ # method='POST',
230
+ # endpoint='/products',
231
+ # payload_str=json.dumps(new_product_data)
232
+ # )
233
+ # print("Product creation result:", create_result)