qfa.api.routes#
API route handlers for the feedback analysis backend.
Functions
|
Analyze a batch of feedback records for trends and themes. |
|
Assign codes: one one-shot pick call, then a separate judge call per level. |
|
Detect sensitive content in feedback items. |
|
Return service health status. |
|
Summarize submitted feedback record. |
|
Summarize all submitted feedback records as a single aggregate summary. |
- async qfa.api.routes.analyze_bulk(body: ApiAnalyzeRequest, request: Request, tenant: TenantApiKey = Depends(dependency=<function authenticate_request>, use_cache=True, scope=None), analyze_service: AnalyzeService = Depends(dependency=<function get_analyze_service>, use_cache=True, scope=None), _scope: CallContext = Depends(dependency=<function call_scope_for.<locals>._scope>, use_cache=True, scope=None)) ApiAnalyzeBulkResponse[source]#
Analyze a batch of feedback records for trends and themes.
The analyst prompt in
body.promptis wrapped in a structural envelope together with the feedback records, and the model is instructed to treat record text as data, not instructions.A second LLM call (AI-as-judge) scores the analysis and produces a natural-language
uncertainty_explanationthe analyst can use to spot unsupported claims. If the judge call fails, the response still returns 200 withquality_score=nulland a constant unavailable message inuncertainty_explanation.Modes:
single_pass(default) — one LLM call within the token cap.hierarchical— embed → cluster → map → reduce pipeline for large corpora (> 5x the single-call token cap). Returns an additionalconfidencefield in the response.
The deterministic
coding_trendstable is populated for both modes — it is built from input metadata and does not depend on the LLM call or chunking. Theperiodrequest field controls the table’s granularity (day/week/month); omit it to use the server-side default.Edge cases:
Input that exceeds the token cap for
single_pass→ 413payload_too_large(usemode=hierarchicalfor large corpora).Records with empty
contentare dropped before analysis (a blank EspoCRM description must not fail the whole batch — issue #138).feedback_record_countreflects the records actually analyzed. If every record is empty the response is a 200 withfeedback_record_count=0and a fallbackanalysisexplaining that no analysis was performed.Injection-like text in record content or metadata is neutralised structurally by the envelope; regex-based detection is a separate guard handled by the LLM adapter.
- Parameters:
body (AnalyzeRequest) – The request body containing feedback records and prompt.
request (Request) – The incoming HTTP request.
tenant (TenantApiKey) – The authenticated tenant, injected via dependency.
analyze_service (AnalyzeService) – The analyze service, injected via dependency.
- Returns:
The analysis result with quality score, uncertainty explanation, feedback record count, and request ID.
coding_trendsis populated for both modes whenever metadata permits;confidenceis populated only forhierarchicalmode.- Return type:
AnalyzeResponse
- async qfa.api.routes.summarize_bulk(body: ApiSummarizeBulkRequest, request: Request, tenant: TenantApiKey = Depends(dependency=<function authenticate_request>, use_cache=True, scope=None), summarize_service: SummarizeService = Depends(dependency=<function get_summarize_service>, use_cache=True, scope=None), _scope: CallContext = Depends(dependency=<function call_scope_for.<locals>._scope>, use_cache=True, scope=None)) ApiSummarizeBulkResponse[source]#
Summarize all submitted feedback records as a single aggregate summary.
Records with empty
contentare dropped before summarization (a blank EspoCRM description must not fail the whole batch — issue #138). If every record is empty the response is a 200 empty aggregate (blanktitle, a fallbacksummaryexplaining that no analysis was performed,quality_score=0.0).- Parameters:
body (ApiSummarizeBulkRequest) – The request body containing feedback records and summarization options.
request (Request) – The incoming HTTP request.
tenant (TenantApiKey) – The authenticated tenant, injected via dependency.
summarize_service (SummarizeService) – The summarisation service, injected via dependency.
- Returns:
A single summary with themes ordered by frequency across all feedback records.
- Return type:
- async qfa.api.routes.summarize(body: ApiSummarizeRequest, request: Request, tenant: TenantApiKey = Depends(dependency=<function authenticate_request>, use_cache=True, scope=None), summarize_service: SummarizeService = Depends(dependency=<function get_summarize_service>, use_cache=True, scope=None), _scope: CallContext = Depends(dependency=<function call_scope_for.<locals>._scope>, use_cache=True, scope=None)) ApiSummarizeResponse[source]#
Summarize submitted feedback record.
If the record’s
contentis empty the response is a 200 empty summary that still echoes the sourceid(blanktitle/summary,quality_score=0.0), returned without an LLM call — a blank EspoCRM description must not produce a silent 422 (issue #138).- Parameters:
body (ApiSummarizeRequest) – The request body containing feedback records and summarization options.
request (Request) – The incoming HTTP request.
tenant (TenantApiKey) – The authenticated tenant, injected via dependency.
summarize_service (SummarizeService) – The summarisation service, injected via dependency.
- Returns:
The per-feedback-record titles and summaries.
- Return type:
- async qfa.api.routes.assign_codes(body: ApiAssignCodesRequest, tenant: TenantApiKey = Depends(dependency=<function authenticate_request>, use_cache=True, scope=None), coding: CodingService = Depends(dependency=<function get_coding_service>, use_cache=True, scope=None), _scope: CallContext = Depends(dependency=<function call_scope_for.<locals>._scope>, use_cache=True, scope=None)) ApiAssignCodesResponse[source]#
Assign codes: one one-shot pick call, then a separate judge call per level.
The full coding framework is flattened into one option per node (at every depth, not just leaves) and a single LLM call picks the best-fitting path(s) directly — no recursive per-level picking. Each selected path is then scored by a separate judge call per level, root to leaf, stopping at the first level that falls below
confidence_threshold.assigned_codesis never an empty list. Whenever no code is applied the response is a 200 carrying exactly one entry whosecoding_level_*/confidence_*fields are null and whoseexplanationbegins with the lineNO CODING APPLIED.followed by the reason (#256), so a client always has something to show the user:the record’s
contentis empty — returned without an LLM call (issue #138);every candidate was filtered out by
confidence_threshold— the explanation names the threshold and lists the closest near misses, highest-scoring first;the classifier selected nothing at all — the explanation states that nothing in the framework was judged relevant.
The explanation is English only, regardless of the feedback language.
- async qfa.api.routes.detect_sensitive(body: ApiDetectSensitiveRequest, request: Request, tenant: TenantApiKey = Depends(dependency=<function authenticate_request>, use_cache=True, scope=None), sensitivity_service: SensitivityService = Depends(dependency=<function get_sensitivity_service>, use_cache=True, scope=None), _scope: CallContext = Depends(dependency=<function call_scope_for.<locals>._scope>, use_cache=True, scope=None)) ApiDetectSensitiveResponse[source]#
Detect sensitive content in feedback items.
If the record’s
contentis empty the response is a 200 reportingis_sensitive=Falsewith nosensitivity_types, returned without an LLM call (issue #138).- Parameters:
body (ApiDetectSensitiveRequest) – The request body containing feedback items to check for sensitive content.
request (Request) – The incoming HTTP request.
tenant (TenantApiKey) – The authenticated tenant, injected via dependency.
sensitivity_service (SensitivityService) – The sensitivity-detection service, injected via dependency.
- Returns:
Sensitivity rating for each submitted feedback item.
- Return type:
- async qfa.api.routes.health() ApiHealthResponse[source]#
Return service health status.
- Returns:
Health status and package version.
- Return type:
HealthResponse