Commit Graph
16 Commits
Author SHA1 Message Date
gongzhiyongandClaude Sonnet 4.6 0f2db39655 fix(backend): handle KB Agent cold-start timeout with retry and graceful errors
KB Agent runs on Azure App Service which cold-starts after idle periods,
taking 20-30s to respond. The previous 15s timeout caused httpx.ReadTimeout
that crashed the ReAct agent tool node and killed the SSE stream.

Changes:
- Increase kb_agent_search_timeout_sec default from 15 to 30 in config.py
- Add single retry with 2s backoff on ReadTimeout in kb_search tool
- Catch all exceptions and return friendly Chinese error messages instead
  of propagating exceptions to the agent loop

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:51:18 +08:00
gongzhiyongandClaude Opus 4.6 1e735202fc fix(backend): wrap SSE astream_events in try/except/finally
Tool call failures (httpx network errors) propagated as unhandled exceptions
through LangGraph TaskGroup, killing SSE stream mid-flight. Frontend received
neither tokens nor done event, leaving it stuck.

- except: sends {"type":"error"} SSE event so frontend shows error message
- finally: persists partial AI content and always sends {"type":"done"}

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 20:36:39 +08:00
gongzhiyongandClaude Sonnet 4.6 e9a11a87e9 fix: set debug=False for production
Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:15:19 +08:00
gongzhiyongandClaude Sonnet 4.6 48d7dd7993 fix: resolve attachment upload 500 errors (delete_attachment startup crash + create_tables ExceptionGroup)
- Add return_dto=None to delete_attachment decorator to fix Litestar
  ImproperlyConfiguredException on 204 status code with None return type
- Catch BaseException instead of Exception in create_tables() to handle
  anyio ExceptionGroup from concurrent gunicorn workers
- Enable debug=True temporarily to capture detailed error traces

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 19:04:50 +08:00
gongzhiyongandClaude Opus 4.6 063d4bf2a0 fix(backend): handle create_tables race condition between gunicorn workers
When 2 gunicorn workers start simultaneously, both call create_all()
which races on CREATE TABLE attachments. The second worker hits a
PostgreSQL UniqueViolation on pg_type_typname_nsp_index because the
type already exists. Wrap create_tables() in try/except to handle
this gracefully.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:48:11 +08:00
gongzhiyongandClaude Opus 4.6 1ff0b9d4ff fix(backend): delete_attachment 204 response must not declare a body
Litestar validates that handlers with 204 status codes cannot return
a response body. The delete_attachment handler was declared as
-> Response and returned Response(content=None, status_code=204),
which caused ImproperlyConfiguredException at startup and crashed
all workers. Change return type to None and move status_code to the
@delete decorator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:39:16 +08:00
gongzhiyongandClaude Sonnet 4.6 712b96e26e feat(backend): implement file upload with Blob Storage, DB model, and Service Bus dispatch
- Add Attachment ORM model (id, conversation_id, message_id, filename, content_type, blob_url, size_bytes, created_at) with FK to conversations
- Add POST /api/attachments/upload (multipart, 50MB limit), GET /api/attachments/{id}, GET /api/attachments/{id}/download (302 to SAS URL), DELETE /api/attachments/{id}
- Add delete_blob() and generate_sas_url() to storage/blob.py for download redirect and cleanup
- Dispatch parse_attachment task to Service Bus for parseable types (PDF, images, CSV, DOCX, XLSX)
- Pre-create blob container on startup (best-effort)
- Add AttachmentOut schema and full API docs in doc/api.md

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 18:30:29 +08:00
gongzhiyongandClaude Sonnet 4.6 433d2361a8 fix(backend): use UUID type for ticket_id parameter in get_ticket handler
Litestar's {ticket_id:uuid} path parameter requires the handler
parameter to be typed as UUID, not str. This caused a 400 validation
error on GET /api/tickets/{uuid}.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:21:55 +08:00
gongzhiyongandClaude Sonnet 4.6 346fa02e44 fix(backend): add azure-storage-blob, azure-servicebus, daytona to requirements
These packages were listed locally but never committed, causing
ModuleNotFoundError: No module named 'azure' on Azure deployment.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:12:02 +08:00
gongzhiyongandClaude Sonnet 4.6 be60d9742e fix(backend): add missing storage, tasks, document, sandbox modules
The app.storage and app.tasks packages were never committed to git,
causing ModuleNotFoundError on Azure deployment. Also adds the
document and sandbox tool modules with their config fields.

Co-Authored-By: Claude Sonnet 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 17:03:30 +08:00
gongzhiyongandClaude Opus 4.6 743aadb771 fix: use uuid type for ticket_id to prevent /summary route conflict
{ticket_id:str} was matching /api/tickets/summary before the literal route.
Changing to {ticket_id:uuid} restricts matching to UUID format only.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:43:15 +08:00
gongzhiyongandClaude Opus 4.6 9f2a144f65 fix(backend): resolve search concurrency race and add tickets summary endpoint
- Replace module-level _current_model global with contextvars.ContextVar
  to prevent concurrent requests from overwriting each other's search
  strategy (flash vs pro)
- Add GET /api/tickets/summary returning {total, by_status, by_priority}
  aggregated from Gongdan API, registered before parameterized ticket routes

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 16:37:42 +08:00
gongzhiyongandClaude Opus 4.6 72f581355c fix: use UUID as ticket id instead of ticketNumber
ticketNumber (e.g. TK-2026-296691) was being used as id, causing
GET /api/tickets/{id} to 404 on upstream Gongdan API which expects UUID.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 15:33:28 +08:00
gongzhiyongandClaude Opus 4.6 6423750f08 fix: add startup.sh with explicit PYTHONPATH for bundled packages
Azure Oryx could not find packages installed to .python_packages/.
Add a startup.sh script that sets PYTHONPATH to include the bundled
package directory before launching gunicorn. Update workflow to set
the Web App startup command to use startup.sh.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:58:07 +08:00
gongzhiyongandClaude Opus 4.6 3759ecd1a6 feat(backend): Phase 3 - Jina search + Redis cache
- Add web_search tool with Jina Search/Reader/Rerank
- Flash mode: top 3, 8s timeout, no Reader/Rerank
- Pro mode: top 10, 20s timeout, concurrent Reader + Rerank top 5
- Add Redis async cache (TTL=300s) for search results
- Register "search" in ALL_TOOLS mapping

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:39:43 +08:00
gongzhiyongandClaude Opus 4.6 efb3c53623 feat: add backend service and Azure deployment workflow
- Add complete Python backend (Litestar + LangGraph) with chat, conversations, tickets APIs
- Add GitHub Actions workflow for auto-deploying backend to Azure Web App (soc-backend)
- Add gunicorn to requirements.txt for production serving
- Update CLAUDE.md and EXTERNAL_SERVICES.md with latest config
- Remove obsolete claudehd.md (merged into gpthd.md)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-08 13:31:00 +08:00