fix: chat agent auth, auto-submit/dispatch, no confirmation prompts

- All httpx tool calls use real user_id instead of uuid(int=0) for
  service token — fixes 401 Unauthorized on dispatch/override
- create_order auto-submits and auto-dispatches in one step
- System prompt updated: execute immediately without asking for
  confirmation, respond in user's language
- Product search returns CAD dimensions (dim_x/y/z_mm)
- query_database tool description includes cad_files schema

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-15 15:59:53 +01:00
parent f70a09886a
commit 0ffc86589a
+45 -17
View File
@@ -29,7 +29,7 @@ You can:
- Check material mapping status
- Query the database for statistics
Always be concise and helpful. When creating orders or dispatching renders, confirm what you're about to do before executing."""
Always be concise and helpful. Execute actions immediately without asking for confirmation — the user expects you to act, not ask. If creating an order, also submit and dispatch it automatically. Combine multiple steps into one action when possible. Respond in the same language the user writes in."""
# ── Tool definitions (OpenAI function-calling schema) ────────────────────────
@@ -276,17 +276,17 @@ async def _execute_tool(
elif name == "create_order":
return await _tool_create_order(db, tenant_id, user_id, **arguments)
elif name == "dispatch_renders":
return await _tool_dispatch_renders(db, tenant_id, **arguments)
return await _tool_dispatch_renders(db, tenant_id, user_id, **arguments)
elif name == "get_order_status":
return await _tool_get_order_status(db, tenant_id, **arguments)
elif name == "set_material_override":
return await _tool_set_material_override(db, tenant_id, **arguments)
return await _tool_set_material_override(db, tenant_id, user_id, **arguments)
elif name == "set_render_overrides":
return await _tool_set_render_overrides(db, tenant_id, **arguments)
return await _tool_set_render_overrides(db, tenant_id, user_id, **arguments)
elif name == "get_render_stats":
return await _tool_get_render_stats(db, tenant_id)
elif name == "check_materials":
return await _tool_check_materials(db, tenant_id, **arguments)
return await _tool_check_materials(db, tenant_id, user_id, **arguments)
elif name == "query_database":
return await _tool_query_database(db, tenant_id, **arguments)
else:
@@ -400,17 +400,45 @@ async def _tool_create_order(
)
resp.raise_for_status()
data = resp.json()
return json.dumps({
"order_id": data["id"],
"order_number": data["order_number"],
order_id = data["id"]
order_number = data["order_number"]
result_info = {
"order_id": order_id,
"order_number": order_number,
"status": data["status"],
"line_count": data.get("line_count", len(lines)),
}, indent=2)
}
# Auto-submit the order
try:
submit_resp = await client.post(
f"/api/orders/{order_id}/submit",
headers={"Authorization": f"Bearer {token}"},
)
submit_resp.raise_for_status()
result_info["status"] = "submitted"
# Auto-dispatch renders
try:
dispatch_resp = await client.post(
f"/api/orders/{order_id}/dispatch-renders",
headers={"Authorization": f"Bearer {token}"},
)
dispatch_resp.raise_for_status()
dispatch_data = dispatch_resp.json()
result_info["status"] = "processing"
result_info["dispatched"] = dispatch_data.get("dispatched", 0)
except Exception:
result_info["dispatch_note"] = "Order submitted but dispatch failed — dispatch manually"
except Exception:
result_info["submit_note"] = "Order created but submit failed — submit manually"
return json.dumps(result_info, indent=2)
except Exception as exc:
return json.dumps({"error": f"Failed to create order: {exc}"})
async def _tool_dispatch_renders(db: AsyncSession, tenant_id: str, order_id: str = "") -> str:
async def _tool_dispatch_renders(db: AsyncSession, tenant_id: str, user_id: str = "", order_id: str = "") -> str:
"""Dispatch renders via internal httpx call."""
import httpx
from app.utils.auth import create_access_token
@@ -423,7 +451,7 @@ async def _tool_dispatch_renders(db: AsyncSession, tenant_id: str, order_id: str
if not check.first():
return json.dumps({"error": "Order not found or not in your tenant"})
token = create_access_token(str(uuid.UUID(int=0)), "global_admin", tenant_id)
token = create_access_token(user_id, "global_admin", tenant_id)
try:
async with httpx.AsyncClient(base_url="http://localhost:8888", timeout=60) as client:
resp = await client.post(
@@ -460,7 +488,7 @@ async def _tool_get_order_status(db: AsyncSession, tenant_id: str, order_id: str
return json.dumps(dict(row), indent=2, default=str)
async def _tool_set_material_override(db: AsyncSession, tenant_id: str, order_id: str = "", material_name: str = "") -> str:
async def _tool_set_material_override(db: AsyncSession, tenant_id: str, user_id: str = "", order_id: str = "", material_name: str = "") -> str:
"""Set material override via internal httpx call."""
import httpx
from app.utils.auth import create_access_token
@@ -472,7 +500,7 @@ async def _tool_set_material_override(db: AsyncSession, tenant_id: str, order_id
if not check.first():
return json.dumps({"error": "Order not found or not in your tenant"})
token = create_access_token(str(uuid.UUID(int=0)), "global_admin", tenant_id)
token = create_access_token(user_id, "global_admin", tenant_id)
try:
async with httpx.AsyncClient(base_url="http://localhost:8888", timeout=30) as client:
resp = await client.post(
@@ -486,7 +514,7 @@ async def _tool_set_material_override(db: AsyncSession, tenant_id: str, order_id
return json.dumps({"error": f"Failed to set material override: {exc}"})
async def _tool_set_render_overrides(db: AsyncSession, tenant_id: str, order_id: str = "", render_overrides: dict | None = None) -> str:
async def _tool_set_render_overrides(db: AsyncSession, tenant_id: str, user_id: str = "", order_id: str = "", render_overrides: dict | None = None) -> str:
"""Set render overrides via internal httpx call."""
import httpx
from app.utils.auth import create_access_token
@@ -498,7 +526,7 @@ async def _tool_set_render_overrides(db: AsyncSession, tenant_id: str, order_id:
if not check.first():
return json.dumps({"error": "Order not found or not in your tenant"})
token = create_access_token(str(uuid.UUID(int=0)), "global_admin", tenant_id)
token = create_access_token(user_id, "global_admin", tenant_id)
try:
async with httpx.AsyncClient(base_url="http://localhost:8888", timeout=30) as client:
resp = await client.post(
@@ -538,7 +566,7 @@ async def _tool_get_render_stats(db: AsyncSession, tenant_id: str) -> str:
return json.dumps(dict(row) if row else {}, indent=2, default=str)
async def _tool_check_materials(db: AsyncSession, tenant_id: str, order_id: str = "") -> str:
async def _tool_check_materials(db: AsyncSession, tenant_id: str, user_id: str = "", order_id: str = "") -> str:
"""Check unmapped materials for an order — uses internal API call."""
import httpx
from app.utils.auth import create_access_token
@@ -550,7 +578,7 @@ async def _tool_check_materials(db: AsyncSession, tenant_id: str, order_id: str
if not check.first():
return json.dumps({"error": "Order not found or not in your tenant"})
token = create_access_token(str(uuid.UUID(int=0)), "global_admin", tenant_id)
token = create_access_token(user_id, "global_admin", tenant_id)
try:
async with httpx.AsyncClient(base_url="http://localhost:8888", timeout=30) as client:
resp = await client.get(