|
|
import gradio as gr
|
|
|
import requests
|
|
|
import json
|
|
|
import asyncio
|
|
|
import logging
|
|
|
from typing import Dict, List, Any, Optional
|
|
|
import anthropic
|
|
|
import openai
|
|
|
from datetime import datetime
|
|
|
import os
|
|
|
|
|
|
|
|
|
|
|
|
def get_api_keys():
|
|
|
|
|
|
openai_key = os.getenv("OPENAI_API_KEY")
|
|
|
dolibarr_key = os.getenv("DOLIBARR_API_KEY")
|
|
|
|
|
|
|
|
|
if not openai_key or not dolibarr_key:
|
|
|
from dotenv import load_dotenv
|
|
|
load_dotenv()
|
|
|
openai_key = os.getenv("OPENAI_API_KEY")
|
|
|
dolibarr_key = os.getenv("DOLIBARR_API_KEY")
|
|
|
|
|
|
|
|
|
if not openai_key:
|
|
|
raise ValueError("OPENAI_API_KEY not found in environment variables or .env file")
|
|
|
if not dolibarr_key:
|
|
|
raise ValueError("DOLIBARR_API_KEY not found in environment variables or .env file")
|
|
|
|
|
|
return openai_key, dolibarr_key
|
|
|
|
|
|
|
|
|
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class DolibarrAPI:
|
|
|
"""Your existing Dolibarr API class - keeping it unchanged"""
|
|
|
base_url = "https://valiant-trust-production.up.railway.app/api/index.php"
|
|
|
|
|
|
def __init__(self, api_key: str):
|
|
|
self.api_key = api_key
|
|
|
self.headers = {
|
|
|
'DOLAPIKEY': api_key,
|
|
|
'Content-Type': 'application/json',
|
|
|
'Accept': 'application/json'
|
|
|
}
|
|
|
|
|
|
def _request(self, method: str, endpoint: str, data: Optional[dict] = None, params: Optional[dict] = None) -> Any:
|
|
|
base_url = "https://valiant-trust-production.up.railway.app/api/index.php"
|
|
|
url = f"{base_url}{endpoint}"
|
|
|
|
|
|
try:
|
|
|
response = requests.request(method, url, headers=self.headers, json=data, params=params)
|
|
|
response.raise_for_status()
|
|
|
return response.json()
|
|
|
except requests.exceptions.RequestException as e:
|
|
|
logger.error(f"API request failed: {e}")
|
|
|
return {"error": f"API request failed: {str(e)}"}
|
|
|
except json.JSONDecodeError as e:
|
|
|
logger.error(f"JSON decode error: {e}")
|
|
|
return {"error": f"Invalid JSON response: {str(e)}"}
|
|
|
|
|
|
def get_req(self, endpoint: str, params: Optional[dict] = None):
|
|
|
return self._request('GET', endpoint, params=params)
|
|
|
|
|
|
def post_req(self, endpoint: str, params: dict):
|
|
|
return self._request("POST", endpoint, data=params)
|
|
|
|
|
|
def put_req(self, endpoint: str, params: dict):
|
|
|
return self._request("PUT", endpoint, data=params)
|
|
|
|
|
|
def del_req(self, endpoint: str, params: Optional[dict] = None):
|
|
|
return self._request("DELETE", endpoint, params=params)
|
|
|
|
|
|
def dolibarr_interface(method: str, endpoint: str, api_key=os.getenv("DOLIBARR_API_KEY"), payload_str: str = "") -> str:
|
|
|
"""Your existing interface function - keeping it unchanged"""
|
|
|
try:
|
|
|
api = DolibarrAPI(api_key)
|
|
|
method = method.upper()
|
|
|
|
|
|
payload = None
|
|
|
if payload_str and payload_str.strip():
|
|
|
try:
|
|
|
payload = json.loads(payload_str)
|
|
|
except json.JSONDecodeError as e:
|
|
|
return json.dumps({"error": f"Invalid JSON payload: {str(e)}"}, indent=2)
|
|
|
|
|
|
if method == 'GET':
|
|
|
result = api.get_req(endpoint, payload)
|
|
|
elif method == 'POST':
|
|
|
if not payload:
|
|
|
return json.dumps({"error": "POST requests require a payload"}, indent=2)
|
|
|
result = api.post_req(endpoint, payload)
|
|
|
elif method == 'PUT':
|
|
|
if not payload:
|
|
|
return json.dumps({"error": "PUT requests require a payload"}, indent=2)
|
|
|
result = api.put_req(endpoint, payload)
|
|
|
elif method == 'DELETE':
|
|
|
result = api.del_req(endpoint, payload)
|
|
|
else:
|
|
|
return json.dumps({"error": f"Invalid HTTP method '{method}' selected."}, indent=2)
|
|
|
|
|
|
return json.dumps(result, indent=2)
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"Unexpected error in dolibarr_interface: {e}")
|
|
|
return json.dumps({"error": f"Unexpected error: {str(e)}"}, indent=2)
|
|
|
|
|
|
class OpenAIDolibarrAgent:
|
|
|
def __init__(self, openai_api_key: str, dolibarr_api_key: str, base_url: str = None):
|
|
|
self.client = openai.OpenAI(api_key=openai_api_key, base_url=base_url)
|
|
|
self.dolibarr_api_key = dolibarr_api_key
|
|
|
|
|
|
|
|
|
self.system_prompt = """You are a helpful ERP assistant that can interact with a Dolibarr system via API calls.
|
|
|
CRITICAL RULES:
|
|
|
1. ALWAYS show ALL data returned by API calls - never truncate, limit, or summarize unless explicitly asked
|
|
|
2. When listing items (customers, invoices, products), display EVERY record returned
|
|
|
3. Present data in clean tables or structured format showing key fields
|
|
|
4. If API returns 100+ records, show all unless user asks for specific filtering
|
|
|
5. NEVER make assumptions about what the user wants to see - show everything
|
|
|
5. DO NOT ASSUME DATA AS IF IT WAS RETURNED BY THE API, NEVER ASSUME
|
|
|
|
|
|
DOLIBARR API ENDPOINTS:
|
|
|
- /thirdparties - GET: list all, GET /{id}: specific customer, POST: create
|
|
|
- /invoices - GET: list all invoices, GET /{id}: specific invoice
|
|
|
- /products - GET: list all products, POST: create product
|
|
|
- /contacts - Contact management
|
|
|
- /users - System users
|
|
|
- /proposals - Commercial proposals/quotes
|
|
|
- /orders - Sales orders
|
|
|
- /bills - Supplier bills
|
|
|
- /projects - Project management
|
|
|
- /stocks - Inventory management
|
|
|
|
|
|
|
|
|
RESPONSE FORMAT RULES:
|
|
|
- For lists: Show ID, Name, Status, and other key fields in table format
|
|
|
- For single items: Show all relevant details clearly organized
|
|
|
- Always extract and display the most important information from API responses
|
|
|
- If API returns error, explain clearly what went wrong
|
|
|
- Include record counts: "Found X customers:" or "Total invoices: Y"
|
|
|
|
|
|
BEHAVIOR RULES:
|
|
|
- Be proactive - if user asks for "customers", get ALL customers
|
|
|
- Don't ask "would you like to see more?" - just show everything
|
|
|
- For specific IDs, show complete details
|
|
|
- When creating records, confirm success with details
|
|
|
- Always make the API call needed - don't hesitate or ask for clarification
|
|
|
|
|
|
Common operations:
|
|
|
- GET /thirdparties - List all customers/suppliers
|
|
|
- GET /thirdparties/{id} - Get specific customer details
|
|
|
- POST /thirdparties - Create new customer
|
|
|
- GET /invoices - List all invoices
|
|
|
- GET /products - List all products
|
|
|
- POST /products - Create new product
|
|
|
|
|
|
You will use the following **HTTP methods**:
|
|
|
|
|
|
* `GET` to retrieve data.
|
|
|
* `POST` to create new records.
|
|
|
* `PUT` to update existing records.
|
|
|
* `DELETE` to delete records.
|
|
|
|
|
|
Here is what you need to do for each of the provided **endpoints**:
|
|
|
|
|
|
---
|
|
|
|
|
|
### **1. `/thirdparties` (Customers, Suppliers, etc.)**
|
|
|
|
|
|
#### **GET** (Retrieve a list or details of a third party):
|
|
|
|
|
|
* **Endpoint**: `/thirdparties` or `/thirdparties/{id}`
|
|
|
* **Required parameters for GET**: No parameters for listing all, but you can use `id` in the endpoint for details of a specific third party.
|
|
|
* **Response**: A list of third parties or details of the specific one.
|
|
|
|
|
|
#### **POST** (Create a new third party):
|
|
|
|
|
|
* **Endpoint**: `/thirdparties`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"name": "John Doe", // The name of the third party
|
|
|
"address": "123 Main St", // Street address
|
|
|
"zip": "12345", // Postal code
|
|
|
"town": "Sample City", // City or town
|
|
|
"country_id": 1, // Country ID (e.g., 1 for USA)
|
|
|
"email": "[email protected]", // Email address
|
|
|
"phone": "+1234567890", // Phone number
|
|
|
"type": 1, // Type (1 for customer, 2 for supplier, etc.)
|
|
|
"status": 1 // Status (1 for active, 0 for inactive)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **PUT** (Update an existing third party):
|
|
|
|
|
|
* **Endpoint**: `/thirdparties/{id}`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"name": "Updated Name", // Update the name or other attributes as needed
|
|
|
"email": "[email protected]", // Update email
|
|
|
"phone": "+9876543210" // Update phone number
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **DELETE** (Delete a third party):
|
|
|
|
|
|
* **Endpoint**: `/thirdparties/{id}`
|
|
|
* **No payload is needed**, just the `id` of the third party to be deleted.
|
|
|
|
|
|
---
|
|
|
|
|
|
### **2. `/invoices`**
|
|
|
|
|
|
#### **GET** (Retrieve a list or details of an invoice):
|
|
|
|
|
|
* **Endpoint**: `/invoices` or `/invoices/{id}`
|
|
|
* **Required parameters for GET**: None for listing all invoices, `id` for a specific invoice.
|
|
|
* **Response**: A list of invoices or details of the specific invoice.
|
|
|
|
|
|
#### **POST** (Create a new invoice):
|
|
|
|
|
|
* **Endpoint**: `/invoices`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"socid": 10, // Third-party ID (Customer ID)
|
|
|
"lines": [ // List of invoice lines
|
|
|
{
|
|
|
"desc": "Web Development Service", // Description of the service/product
|
|
|
"subprice": 500, // Unit price
|
|
|
"qty": 1, // Quantity
|
|
|
"total_ht": 500, // Total excluding tax
|
|
|
"vat": 18, // VAT percentage
|
|
|
"total_ttc": 590 // Total including tax
|
|
|
}
|
|
|
],
|
|
|
"date": "2025-06-01", // Invoice creation date (YYYY-MM-DD)
|
|
|
"duedate": "2025-06-15" // Due date (YYYY-MM-DD)
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **PUT** (Update an existing invoice):
|
|
|
|
|
|
* **Endpoint**: `/invoices/{id}`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"lines": [ // Updated lines
|
|
|
{
|
|
|
"desc": "Updated Service", // New or updated description
|
|
|
"subprice": 550, // New price
|
|
|
"qty": 2, // Updated quantity
|
|
|
"total_ht": 1100, // Updated total excluding tax
|
|
|
"vat": 18, // VAT
|
|
|
"total_ttc": 1294 // Updated total including tax
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **DELETE** (Delete an invoice):
|
|
|
|
|
|
* **Endpoint**: `/invoices/{id}`
|
|
|
* **No payload needed**, just the `id` of the invoice to delete.
|
|
|
|
|
|
---
|
|
|
|
|
|
### **3. `/contacts` (Contacts for Third Parties)**
|
|
|
|
|
|
#### **GET** (Retrieve a list or details of a contact):
|
|
|
|
|
|
* **Endpoint**: `/contacts` or `/contacts/{id}`
|
|
|
* **Required parameters for GET**: None for listing all contacts, `id` for a specific contact.
|
|
|
* **Response**: A list of contacts or details of a specific contact.
|
|
|
|
|
|
#### **POST** (Create a new contact):
|
|
|
|
|
|
* **Endpoint**: `/contacts`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"thirdparty_id": 1, // Third-party ID (Customer or Supplier)
|
|
|
"firstname": "Jane", // Contact first name
|
|
|
"lastname": "Doe", // Contact last name
|
|
|
"email": "[email protected]", // Email address
|
|
|
"phone": "+1234567890", // Phone number
|
|
|
"position": "Sales Manager", // Position of the contact
|
|
|
"address": "1234 Office St" // Address
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **PUT** (Update an existing contact):
|
|
|
|
|
|
* **Endpoint**: `/contacts/{id}`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"email": "[email protected]", // Update email
|
|
|
"phone": "+9876543210" // Update phone
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **DELETE** (Delete a contact):
|
|
|
|
|
|
* **Endpoint**: `/contacts/{id}`
|
|
|
* **No payload needed**, just the `id` of the contact to delete.
|
|
|
|
|
|
---
|
|
|
|
|
|
### **4. `/orders` (Customer/Supplier Orders)**
|
|
|
|
|
|
#### **GET** (Retrieve a list or details of an order):
|
|
|
|
|
|
* **Endpoint**: `/orders` or `/orders/{id}`
|
|
|
* **Required parameters for GET**: None for listing all orders, `id` for a specific order.
|
|
|
* **Response**: A list of orders or details of a specific order.
|
|
|
|
|
|
#### **POST** (Create a new order):
|
|
|
|
|
|
* **Endpoint**: `/orders`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"socid": 10, // Third-party ID (Customer ID)
|
|
|
"lines": [ // List of order lines
|
|
|
{
|
|
|
"desc": "Laptop", // Description of product
|
|
|
"subprice": 1000, // Unit price
|
|
|
"qty": 1, // Quantity
|
|
|
"total_ht": 1000, // Total excluding tax
|
|
|
"vat": 18, // VAT
|
|
|
"total_ttc": 1180 // Total including tax
|
|
|
}
|
|
|
],
|
|
|
"date": "2025-06-01", // Order date
|
|
|
"duedate": "2025-06-15" // Due date
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **PUT** (Update an existing order):
|
|
|
|
|
|
* **Endpoint**: `/orders/{id}`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"lines": [ // Updated lines
|
|
|
{
|
|
|
"desc": "Updated Laptop", // Updated description
|
|
|
"subprice": 1100, // Updated price
|
|
|
"qty": 2, // Updated quantity
|
|
|
"total_ht": 2200, // Updated total excluding tax
|
|
|
"vat": 18, // VAT
|
|
|
"total_ttc": 2600 // Updated total including tax
|
|
|
}
|
|
|
]
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **DELETE** (Delete an order):
|
|
|
|
|
|
* **Endpoint**: `/orders/{id}`
|
|
|
* **No payload needed**, just the `id` of the order to delete.
|
|
|
|
|
|
---
|
|
|
|
|
|
### **5. `/products`**
|
|
|
|
|
|
#### **GET** (Retrieve a list or details of a product):
|
|
|
|
|
|
* **Endpoint**: `/products` or `/products/{id}`
|
|
|
* **Required parameters for GET**: None for listing all products, `id` for a specific product.
|
|
|
* **Response**: A list of products or details of a specific product.
|
|
|
|
|
|
#### **POST** (Create a new product):
|
|
|
|
|
|
* **Endpoint**: `/products`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"label": "Smartphone", // Product
|
|
|
```
|
|
|
|
|
|
|
|
|
name
|
|
|
"price": 499.99, // Unit price
|
|
|
"stock": 100, // Quantity in stock
|
|
|
"description": "Latest model", // Product description
|
|
|
"socid": 10 // Supplier ID
|
|
|
}
|
|
|
|
|
|
|
|
|
#### **PUT** (Update an existing product):
|
|
|
|
|
|
* **Endpoint**: `/products/{id}`
|
|
|
* **Required Parameters** (in `payload` JSON):
|
|
|
|
|
|
```json
|
|
|
{
|
|
|
"label": "Updated Smartphone", // Updated product name
|
|
|
"price": 549.99, // Updated price
|
|
|
"stock": 120, // Updated stock
|
|
|
"description": "Updated model" // Updated description
|
|
|
}
|
|
|
```
|
|
|
|
|
|
#### **DELETE** (Delete a product):
|
|
|
|
|
|
* **Endpoint**: `/products/{id}`
|
|
|
* **No payload needed**, just the `id` of the product to delete.
|
|
|
|
|
|
# When a product/invoice/thirdparty is mentioned, call api with a get request to get their id and name and then proceed to make the next required api call.
|
|
|
|
|
|
When users ask for information, determine the appropriate API call needed and use the dolibarr_api function.
|
|
|
Always format responses in a user-friendly way, extracting key information from the API responses.
|
|
|
If an API call fails, explain the error clearly and suggest alternatives.
|
|
|
|
|
|
Current date: """ + datetime.now().strftime("%Y-%m-%d")
|
|
|
|
|
|
|
|
|
self.functions = [
|
|
|
{
|
|
|
"name": "dolibarr_api",
|
|
|
"description": "Execute API calls to the Dolibarr ERP system",
|
|
|
"parameters": {
|
|
|
"type": "object",
|
|
|
"properties": {
|
|
|
"method": {
|
|
|
"type": "string",
|
|
|
"enum": ["GET", "POST", "PUT", "DELETE"],
|
|
|
"description": "HTTP method for the API call"
|
|
|
},
|
|
|
"endpoint": {
|
|
|
"type": "string",
|
|
|
"description": "API endpoint (e.g., /thirdparties, /invoices)"
|
|
|
},
|
|
|
"payload": {
|
|
|
"type": "string",
|
|
|
"description": "JSON payload for POST/PUT requests (leave empty for GET)"
|
|
|
}
|
|
|
},
|
|
|
"required": ["method", "endpoint"]
|
|
|
}
|
|
|
}
|
|
|
]
|
|
|
|
|
|
def execute_dolibarr_call(self, method: str, endpoint: str, payload: str = "") -> str:
|
|
|
"""Execute the actual Dolibarr API call"""
|
|
|
return dolibarr_interface(method, endpoint, self.dolibarr_api_key, payload)
|
|
|
|
|
|
def chat(self, message: str, history: List[List[str]]) -> str:
|
|
|
"""Main chat function that processes user messages"""
|
|
|
try:
|
|
|
|
|
|
messages = [{"role": "system", "content": self.system_prompt}]
|
|
|
|
|
|
for human_msg, assistant_msg in history:
|
|
|
if human_msg:
|
|
|
messages.append({"role": "user", "content": human_msg})
|
|
|
if assistant_msg:
|
|
|
messages.append({"role": "assistant", "content": assistant_msg})
|
|
|
|
|
|
|
|
|
messages.append({"role": "user", "content": message})
|
|
|
|
|
|
|
|
|
logger.info("Sending request to Nebius API...")
|
|
|
response = self.client.chat.completions.create(
|
|
|
model="gpt-3.5-turbo",
|
|
|
messages=messages,
|
|
|
functions=self.functions,
|
|
|
function_call="auto",
|
|
|
max_tokens=1500
|
|
|
)
|
|
|
|
|
|
|
|
|
message = response.choices[0].message
|
|
|
logger.info(f"Received response from Nebius: {message}")
|
|
|
|
|
|
if message.function_call:
|
|
|
|
|
|
function_name = message.function_call.name
|
|
|
function_args = json.loads(message.function_call.arguments)
|
|
|
logger.info(f"Function call: {function_name} with args: {function_args}")
|
|
|
|
|
|
if function_name == "dolibarr_api":
|
|
|
api_result = self.execute_dolibarr_call(
|
|
|
method=function_args.get("method", "GET"),
|
|
|
endpoint=function_args.get("endpoint", ""),
|
|
|
payload=function_args.get("payload", "")
|
|
|
)
|
|
|
logger.info(f"Dolibarr API result: {api_result}")
|
|
|
|
|
|
messages.append({
|
|
|
"role": "assistant",
|
|
|
"content": None,
|
|
|
"function_call": message.function_call
|
|
|
})
|
|
|
messages.append({
|
|
|
"role": "function",
|
|
|
"name": function_name,
|
|
|
"content": api_result
|
|
|
})
|
|
|
|
|
|
|
|
|
logger.info("Getting final response from Nebius...")
|
|
|
final_response = self.client.chat.completions.create(
|
|
|
model="gpt-3.5-turbo",
|
|
|
messages=messages,
|
|
|
max_tokens=1500
|
|
|
)
|
|
|
logger.info(f"Final response: {final_response.choices[0].message}")
|
|
|
|
|
|
|
|
|
content = final_response.choices[0].message.content
|
|
|
|
|
|
content = content.split('</think>')[-1].strip() if '</think>' in content else content
|
|
|
return content
|
|
|
|
|
|
|
|
|
content = message.content
|
|
|
content = content.split('</think>')[-1].strip() if '</think>' in content else content
|
|
|
return content if content else "I couldn't process that request."
|
|
|
|
|
|
except openai.APIConnectionError as e:
|
|
|
logger.error(f"OpenAI API Connection Error: {e}")
|
|
|
return "Sorry, I'm having trouble connecting to OpenAI. Please check if the API key is valid and the service is available."
|
|
|
except openai.AuthenticationError as e:
|
|
|
logger.error(f"OpenAI API Authentication Error: {e}")
|
|
|
return "Sorry, there's an authentication error with the OpenAI API. Please check if the API key is correct."
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error in chat: {e}")
|
|
|
return f"Sorry, I encountered an error: {str(e)}"
|
|
|
|
|
|
def create_openai_agent_interface():
|
|
|
"""Create the Gradio interface for the OpenAI-powered Dolibarr agent"""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
OPENAI_API_KEY, DOLIBARR_API_KEY = get_api_keys()
|
|
|
|
|
|
|
|
|
logger.info("API Keys loaded successfully")
|
|
|
logger.info(f"OpenAI API Key length: {len(OPENAI_API_KEY) if OPENAI_API_KEY else 0}")
|
|
|
logger.info(f"Dolibarr API Key length: {len(DOLIBARR_API_KEY) if DOLIBARR_API_KEY else 0}")
|
|
|
|
|
|
if not OPENAI_API_KEY or not DOLIBARR_API_KEY:
|
|
|
raise ValueError("API keys not found. Please set them in Hugging Face Secrets or .env file")
|
|
|
|
|
|
|
|
|
agent = OpenAIDolibarrAgent(OPENAI_API_KEY, DOLIBARR_API_KEY)
|
|
|
agent = OpenAIDolibarrAgent(OPENAI_API_KEY, DOLIBARR_API_KEY)
|
|
|
|
|
|
|
|
|
|
|
|
demo = gr.ChatInterface(
|
|
|
fn=agent.chat,
|
|
|
title="π€ ERP Assistant",
|
|
|
description="""
|
|
|
π€ AI-Powered Dolibarr ERP Assistant - Your intelligent business management companion. I can help you manage customers, invoices, products, orders, and financial operations through natural conversation. Simply type your request (e.g., "Show me all customers" or "Create a new invoice") and get instant results. Try it with our demo instance at https://valiant-trust-production.up.railway.app/ (username: admin, password: admin123).
|
|
|
""",
|
|
|
examples=[
|
|
|
"Show me all customers",
|
|
|
"List all invoices",
|
|
|
"What products do we have?",
|
|
|
"Get details for customer ID 1",
|
|
|
"Show me recent proposals"
|
|
|
],
|
|
|
cache_examples=False,
|
|
|
theme=gr.themes.Soft()
|
|
|
)
|
|
|
|
|
|
return demo
|
|
|
|
|
|
|
|
|
if __name__ == '__main__':
|
|
|
try:
|
|
|
print("π Starting OpenAI-Powered Dolibarr Agent...")
|
|
|
|
|
|
|
|
|
demo = create_openai_agent_interface()
|
|
|
demo.launch(
|
|
|
server_name="127.0.0.1",
|
|
|
server_port=7862,
|
|
|
share=False,
|
|
|
debug=True,
|
|
|
show_error=True
|
|
|
)
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"Failed to start application: {e}")
|
|
|
print(f"β Error starting application: {e}")
|
|
|
|
|
|
|
|
|
"""
|
|
|
- "Show me all customers"
|
|
|
- "List all invoices"
|
|
|
- "Get me customer details for ID 1"
|
|
|
- "What products do we have?"
|
|
|
- "Show me recent proposals"
|
|
|
- "Create a new customer named Test Corp"
|
|
|
- "Find all unpaid invoices"
|
|
|
"""
|
|
|
|