hazo_collab_forms 3.1.6 → 5.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGE_LOG.md +207 -0
- package/README.md +3 -0
- package/dist/components/clarification/clarification_item_body.d.ts +19 -1
- package/dist/components/clarification/clarification_item_body.d.ts.map +1 -1
- package/dist/components/clarification/clarification_item_body.js +114 -6
- package/dist/components/clarification/clarification_item_body.js.map +1 -1
- package/dist/components/clarification/clarification_thread.js +1 -1
- package/dist/components/clarification/clarification_thread.js.map +1 -1
- package/dist/components/clarification/index.d.ts +2 -0
- package/dist/components/clarification/index.d.ts.map +1 -1
- package/dist/components/clarification/index.js +1 -0
- package/dist/components/clarification/index.js.map +1 -1
- package/dist/components/clarification/resolution_status_strip.d.ts +18 -0
- package/dist/components/clarification/resolution_status_strip.d.ts.map +1 -0
- package/dist/components/clarification/resolution_status_strip.js +20 -0
- package/dist/components/clarification/resolution_status_strip.js.map +1 -0
- package/dist/components/hazo_fb_form/context.d.ts +1 -1
- package/dist/components/hazo_fb_form/context.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hazo_fb_form.js +330 -113
- package/dist/components/hazo_fb_form/hazo_fb_form.js.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts +3 -3
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js +340 -61
- package/dist/components/hazo_fb_form/hooks/use_fb_form_state.js.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts +3 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js +89 -11
- package/dist/components/hazo_fb_form/hooks/use_llm_run.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.js +1 -1
- package/dist/components/hazo_fb_form/shared/agent_stepper.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts +9 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.d.ts.map +1 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js +39 -0
- package/dist/components/hazo_fb_form/shared/file_status_accordion.js.map +1 -0
- package/dist/components/hazo_fb_form/shared/format.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/shared/format.js +8 -3
- package/dist/components/hazo_fb_form/shared/format.js.map +1 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts +7 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js +6 -3
- package/dist/components/hazo_fb_form/shared/send_back_item_card.js.map +1 -1
- package/dist/components/hazo_fb_form/types.d.ts +3 -1
- package/dist/components/hazo_fb_form/types.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js +1 -1
- package/dist/components/hazo_fb_form/views/back_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/clarifications_view.js +2 -2
- package/dist/components/hazo_fb_form/views/clarifications_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/front_office_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/front_office_view.js +62 -41
- package/dist/components/hazo_fb_form/views/front_office_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/interim_view.js +3 -3
- package/dist/components/hazo_fb_form/views/interim_view.js.map +1 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.d.ts.map +1 -1
- package/dist/components/hazo_fb_form/views/review_queue_view.js +22 -9
- package/dist/components/hazo_fb_form/views/review_queue_view.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js +32 -3
- package/dist/components/hazo_validation_rule_editor/components/rule_editor.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts +20 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.d.ts.map +1 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js +34 -0
- package/dist/components/hazo_validation_rule_editor/components/variable_chain_input.js.map +1 -0
- package/dist/components/hazo_validation_rule_editor/context.d.ts +3 -2
- package/dist/components/hazo_validation_rule_editor/context.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/context.js +15 -3
- package/dist/components/hazo_validation_rule_editor/context.js.map +1 -1
- package/dist/components/hazo_validation_rule_editor/types.d.ts +7 -1
- package/dist/components/hazo_validation_rule_editor/types.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.d.ts.map +1 -1
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js +2 -2
- package/dist/components/hazo_validation_rule_editor/validation_rule_editor.js.map +1 -1
- package/dist/components/index.d.ts +2 -0
- package/dist/components/index.d.ts.map +1 -1
- package/dist/components/index.js +2 -0
- package/dist/components/index.js.map +1 -1
- package/dist/components/shared/document_type_editor.d.ts +31 -0
- package/dist/components/shared/document_type_editor.d.ts.map +1 -0
- package/dist/components/shared/document_type_editor.js +60 -0
- package/dist/components/shared/document_type_editor.js.map +1 -0
- package/dist/components/shared/file_bar/file_bar.d.ts +7 -1
- package/dist/components/shared/file_bar/file_bar.d.ts.map +1 -1
- package/dist/components/shared/file_bar/file_bar.js +5 -3
- package/dist/components/shared/file_bar/file_bar.js.map +1 -1
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js +4 -4
- package/dist/components/shared/file_bar/file_bar_validation_dialog.js.map +1 -1
- package/dist/components/shared/file_status_icon.d.ts +23 -0
- package/dist/components/shared/file_status_icon.d.ts.map +1 -0
- package/dist/components/shared/file_status_icon.js +38 -0
- package/dist/components/shared/file_status_icon.js.map +1 -0
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts +1 -1
- package/dist/components/shared/json_data_panel/json_data_panel.d.ts.map +1 -1
- package/dist/components/shared/json_data_panel/json_data_panel.js +27 -2
- package/dist/components/shared/json_data_panel/json_data_panel.js.map +1 -1
- package/dist/components/shared/rule_result_card.d.ts.map +1 -1
- package/dist/components/shared/rule_result_card.js +5 -4
- package/dist/components/shared/rule_result_card.js.map +1 -1
- package/dist/components/thread_form/components/add_question_dialog.d.ts +12 -0
- package/dist/components/thread_form/components/add_question_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/add_question_dialog.js +36 -0
- package/dist/components/thread_form/components/add_question_dialog.js.map +1 -0
- package/dist/components/thread_form/components/agent_compose_dialog.d.ts +30 -0
- package/dist/components/thread_form/components/agent_compose_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/agent_compose_dialog.js +45 -0
- package/dist/components/thread_form/components/agent_compose_dialog.js.map +1 -0
- package/dist/components/thread_form/components/clarification.d.ts +14 -0
- package/dist/components/thread_form/components/clarification.d.ts.map +1 -0
- package/dist/components/thread_form/components/clarification.js +12 -0
- package/dist/components/thread_form/components/clarification.js.map +1 -0
- package/dist/components/thread_form/components/collected_data_view.d.ts +15 -0
- package/dist/components/thread_form/components/collected_data_view.d.ts.map +1 -0
- package/dist/components/thread_form/components/collected_data_view.js +121 -0
- package/dist/components/thread_form/components/collected_data_view.js.map +1 -0
- package/dist/components/thread_form/components/coverage_card.d.ts +11 -0
- package/dist/components/thread_form/components/coverage_card.d.ts.map +1 -0
- package/dist/components/thread_form/components/coverage_card.js +60 -0
- package/dist/components/thread_form/components/coverage_card.js.map +1 -0
- package/dist/components/thread_form/components/file_bar.d.ts +93 -0
- package/dist/components/thread_form/components/file_bar.d.ts.map +1 -0
- package/dist/components/thread_form/components/file_bar.js +251 -0
- package/dist/components/thread_form/components/file_bar.js.map +1 -0
- package/dist/components/thread_form/components/file_info_dialog.d.ts +15 -0
- package/dist/components/thread_form/components/file_info_dialog.d.ts.map +1 -0
- package/dist/components/thread_form/components/file_info_dialog.js +64 -0
- package/dist/components/thread_form/components/file_info_dialog.js.map +1 -0
- package/dist/components/thread_form/components/issue_group_tree.d.ts +20 -0
- package/dist/components/thread_form/components/issue_group_tree.d.ts.map +1 -0
- package/dist/components/thread_form/components/issue_group_tree.js +164 -0
- package/dist/components/thread_form/components/issue_group_tree.js.map +1 -0
- package/dist/components/thread_form/components/pdf_side_panel.d.ts +20 -0
- package/dist/components/thread_form/components/pdf_side_panel.d.ts.map +1 -0
- package/dist/components/thread_form/components/pdf_side_panel.js +63 -0
- package/dist/components/thread_form/components/pdf_side_panel.js.map +1 -0
- package/dist/components/thread_form/components/rule_decision_row.d.ts +31 -0
- package/dist/components/thread_form/components/rule_decision_row.d.ts.map +1 -0
- package/dist/components/thread_form/components/rule_decision_row.js +20 -0
- package/dist/components/thread_form/components/rule_decision_row.js.map +1 -0
- package/dist/components/thread_form/components/send_back_message.d.ts +32 -0
- package/dist/components/thread_form/components/send_back_message.d.ts.map +1 -0
- package/dist/components/thread_form/components/send_back_message.js +82 -0
- package/dist/components/thread_form/components/send_back_message.js.map +1 -0
- package/dist/components/thread_form/components/shared.d.ts +54 -0
- package/dist/components/thread_form/components/shared.d.ts.map +1 -0
- package/dist/components/thread_form/components/shared.js +136 -0
- package/dist/components/thread_form/components/shared.js.map +1 -0
- package/dist/components/thread_form/components/task_card.d.ts +90 -0
- package/dist/components/thread_form/components/task_card.d.ts.map +1 -0
- package/dist/components/thread_form/components/task_card.js +63 -0
- package/dist/components/thread_form/components/task_card.js.map +1 -0
- package/dist/components/thread_form/components/text_doc_check.d.ts +15 -0
- package/dist/components/thread_form/components/text_doc_check.d.ts.map +1 -0
- package/dist/components/thread_form/components/text_doc_check.js +16 -0
- package/dist/components/thread_form/components/text_doc_check.js.map +1 -0
- package/dist/components/thread_form/components/text_extraction.d.ts +14 -0
- package/dist/components/thread_form/components/text_extraction.d.ts.map +1 -0
- package/dist/components/thread_form/components/text_extraction.js +16 -0
- package/dist/components/thread_form/components/text_extraction.js.map +1 -0
- package/dist/components/thread_form/components/thread_composer.d.ts +15 -0
- package/dist/components/thread_form/components/thread_composer.d.ts.map +1 -0
- package/dist/components/thread_form/components/thread_composer.js +93 -0
- package/dist/components/thread_form/components/thread_composer.js.map +1 -0
- package/dist/components/thread_form/components/thread_timeline.d.ts +65 -0
- package/dist/components/thread_form/components/thread_timeline.d.ts.map +1 -0
- package/dist/components/thread_form/components/thread_timeline.js +225 -0
- package/dist/components/thread_form/components/thread_timeline.js.map +1 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts +126 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.d.ts.map +1 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.js +760 -0
- package/dist/components/thread_form/hooks/use_file_pipeline.js.map +1 -0
- package/dist/components/thread_form/hooks/use_thread_form.d.ts +36 -0
- package/dist/components/thread_form/hooks/use_thread_form.d.ts.map +1 -0
- package/dist/components/thread_form/hooks/use_thread_form.js +126 -0
- package/dist/components/thread_form/hooks/use_thread_form.js.map +1 -0
- package/dist/components/thread_form/index.d.ts +33 -0
- package/dist/components/thread_form/index.d.ts.map +1 -0
- package/dist/components/thread_form/index.js +30 -0
- package/dist/components/thread_form/index.js.map +1 -0
- package/dist/components/thread_form/sample_data.d.ts +8 -0
- package/dist/components/thread_form/sample_data.d.ts.map +1 -0
- package/dist/components/thread_form/sample_data.js +658 -0
- package/dist/components/thread_form/sample_data.js.map +1 -0
- package/dist/components/thread_form/thread_form.d.ts +7 -0
- package/dist/components/thread_form/thread_form.d.ts.map +1 -0
- package/dist/components/thread_form/thread_form.js +1385 -0
- package/dist/components/thread_form/thread_form.js.map +1 -0
- package/dist/components/thread_form/types.d.ts +402 -0
- package/dist/components/thread_form/types.d.ts.map +1 -0
- package/dist/components/thread_form/types.js +23 -0
- package/dist/components/thread_form/types.js.map +1 -0
- package/dist/components/thread_form/utils/file_decision_state.d.ts +22 -0
- package/dist/components/thread_form/utils/file_decision_state.d.ts.map +1 -0
- package/dist/components/thread_form/utils/file_decision_state.js +37 -0
- package/dist/components/thread_form/utils/file_decision_state.js.map +1 -0
- package/dist/components/thread_form/utils/merge_send_back.d.ts +13 -0
- package/dist/components/thread_form/utils/merge_send_back.d.ts.map +1 -0
- package/dist/components/thread_form/utils/merge_send_back.js +23 -0
- package/dist/components/thread_form/utils/merge_send_back.js.map +1 -0
- package/dist/lib/autofill_handler.d.ts.map +1 -1
- package/dist/lib/autofill_handler.js +5 -44
- package/dist/lib/autofill_handler.js.map +1 -1
- package/dist/lib/classification_handler.d.ts +105 -0
- package/dist/lib/classification_handler.d.ts.map +1 -0
- package/dist/lib/classification_handler.js +342 -0
- package/dist/lib/classification_handler.js.map +1 -0
- package/dist/lib/content_gate_handler.d.ts +37 -0
- package/dist/lib/content_gate_handler.d.ts.map +1 -0
- package/dist/lib/content_gate_handler.js +126 -0
- package/dist/lib/content_gate_handler.js.map +1 -0
- package/dist/lib/index.d.ts +10 -0
- package/dist/lib/index.d.ts.map +1 -1
- package/dist/lib/index.js +5 -0
- package/dist/lib/index.js.map +1 -1
- package/dist/lib/periodic_coverage_runner.d.ts +24 -0
- package/dist/lib/periodic_coverage_runner.d.ts.map +1 -0
- package/dist/lib/periodic_coverage_runner.js +121 -0
- package/dist/lib/periodic_coverage_runner.js.map +1 -0
- package/dist/lib/resolution_handler.d.ts +150 -0
- package/dist/lib/resolution_handler.d.ts.map +1 -0
- package/dist/lib/resolution_handler.js +597 -0
- package/dist/lib/resolution_handler.js.map +1 -0
- package/dist/lib/resolve_variable.d.ts +25 -0
- package/dist/lib/resolve_variable.d.ts.map +1 -0
- package/dist/lib/resolve_variable.js +77 -0
- package/dist/lib/resolve_variable.js.map +1 -0
- package/dist/lib/validation_handler.d.ts +27 -3
- package/dist/lib/validation_handler.d.ts.map +1 -1
- package/dist/lib/validation_handler.js +338 -288
- package/dist/lib/validation_handler.js.map +1 -1
- package/dist/types/clarification.d.ts +54 -0
- package/dist/types/clarification.d.ts.map +1 -1
- package/dist/types/fb_form_data.d.ts +273 -123
- package/dist/types/fb_form_data.d.ts.map +1 -1
- package/dist/types/fb_form_data.js +44 -58
- package/dist/types/fb_form_data.js.map +1 -1
- package/dist/types/fb_form_data_v1.d.ts +250 -0
- package/dist/types/fb_form_data_v1.d.ts.map +1 -0
- package/dist/types/fb_form_data_v1.js +117 -0
- package/dist/types/fb_form_data_v1.js.map +1 -0
- package/dist/types/fb_form_instance.d.ts +1 -1
- package/dist/types/fb_form_instance.d.ts.map +1 -1
- package/dist/types/index.d.ts +5 -3
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +2 -1
- package/dist/types/index.js.map +1 -1
- package/dist/types/validation.d.ts +134 -12
- package/dist/types/validation.d.ts.map +1 -1
- package/dist/utils/expectation_extractor.d.ts +31 -0
- package/dist/utils/expectation_extractor.d.ts.map +1 -0
- package/dist/utils/expectation_extractor.js +142 -0
- package/dist/utils/expectation_extractor.js.map +1 -0
- package/dist/utils/fb_data_adapter.d.ts +7 -2
- package/dist/utils/fb_data_adapter.d.ts.map +1 -1
- package/dist/utils/fb_data_adapter.js +58 -7
- package/dist/utils/fb_data_adapter.js.map +1 -1
- package/dist/utils/fb_data_adapter_v2.d.ts +17 -0
- package/dist/utils/fb_data_adapter_v2.d.ts.map +1 -0
- package/dist/utils/fb_data_adapter_v2.js +483 -0
- package/dist/utils/fb_data_adapter_v2.js.map +1 -0
- package/dist/utils/fb_data_helpers.d.ts +1 -1
- package/dist/utils/fb_data_helpers.d.ts.map +1 -1
- package/dist/utils/fb_data_mutations.d.ts +1 -1
- package/dist/utils/fb_data_mutations.d.ts.map +1 -1
- package/dist/utils/fb_data_mutations_v2.d.ts +46 -0
- package/dist/utils/fb_data_mutations_v2.d.ts.map +1 -0
- package/dist/utils/fb_data_mutations_v2.js +341 -0
- package/dist/utils/fb_data_mutations_v2.js.map +1 -0
- package/dist/utils/fb_data_queries.d.ts +81 -0
- package/dist/utils/fb_data_queries.d.ts.map +1 -0
- package/dist/utils/fb_data_queries.js +354 -0
- package/dist/utils/fb_data_queries.js.map +1 -0
- package/dist/utils/index.d.ts +4 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +6 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/issue_bucketing.d.ts +36 -0
- package/dist/utils/issue_bucketing.d.ts.map +1 -0
- package/dist/utils/issue_bucketing.js +107 -0
- package/dist/utils/issue_bucketing.js.map +1 -0
- package/dist/utils/validation_result.d.ts +32 -0
- package/dist/utils/validation_result.d.ts.map +1 -0
- package/dist/utils/validation_result.js +55 -0
- package/dist/utils/validation_result.js.map +1 -0
- package/package.json +16 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file_decision_state.js","sourceRoot":"","sources":["../../../../src/components/thread_form/utils/file_decision_state.ts"],"names":[],"mappings":"AAaA;;;;;;;;GAQG;AACH,MAAM,UAAU,2BAA2B,CACzC,YAAoD,EACpD,iBAAyC;IAEzC,MAAM,OAAO,GAAG,YAAY,IAAI,EAAE,CAAC;IACnC,MAAM,KAAK,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAEjE,IAAI,aAAa,GAAG,KAAK,CAAC;IAC1B,IAAI,YAAY,GAAG,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC;IACpC,IAAI,aAAa,GAAG,CAAC,CAAC;IAEtB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,MAAM,KAAK,WAAW,EAAE,CAAC;YAC3B,aAAa,GAAG,IAAI,CAAC;YACrB,YAAY,GAAG,KAAK,CAAC;QACvB,CAAC;aAAM,IAAI,MAAM,KAAK,UAAU,EAAE,CAAC;YACjC,WAAW;QACb,CAAC;aAAM,CAAC;YACN,UAAU;YACV,YAAY,GAAG,KAAK,CAAC;YACrB,aAAa,IAAI,IAAI,CAAC,MAAM,CAAC,MAAM,CAAC;QACtC,CAAC;IACH,CAAC;IAED,OAAO;QACL,QAAQ,EAAE,YAAY,IAAI,CAAC,aAAa;QACxC,SAAS,EAAE,aAAa;QACxB,aAAa;KACd,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ThreadTask, ThreadMessage } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Decide where a new send_back message should land in the form's tasks array.
|
|
4
|
+
*
|
|
5
|
+
* If there is already an open clarifications task (one whose `parent_task_id`
|
|
6
|
+
* matches and whose `status !== 'completed'`), append the message to that task.
|
|
7
|
+
* Otherwise, ask the caller to build a fresh clarifications task and append it
|
|
8
|
+
* to the array.
|
|
9
|
+
*
|
|
10
|
+
* Pure: returns a new array; never mutates the input.
|
|
11
|
+
*/
|
|
12
|
+
export declare function merge_send_back_message(tasks: ThreadTask[], parent_task_id: string, new_msg: ThreadMessage, build_new_task: (msg: ThreadMessage) => ThreadTask): ThreadTask[];
|
|
13
|
+
//# sourceMappingURL=merge_send_back.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge_send_back.d.ts","sourceRoot":"","sources":["../../../../src/components/thread_form/utils/merge_send_back.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAE7D;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,EAAE,UAAU,EAAE,EACnB,cAAc,EAAE,MAAM,EACtB,OAAO,EAAE,aAAa,EACtB,cAAc,EAAE,CAAC,GAAG,EAAE,aAAa,KAAK,UAAU,GACjD,UAAU,EAAE,CAed"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decide where a new send_back message should land in the form's tasks array.
|
|
3
|
+
*
|
|
4
|
+
* If there is already an open clarifications task (one whose `parent_task_id`
|
|
5
|
+
* matches and whose `status !== 'completed'`), append the message to that task.
|
|
6
|
+
* Otherwise, ask the caller to build a fresh clarifications task and append it
|
|
7
|
+
* to the array.
|
|
8
|
+
*
|
|
9
|
+
* Pure: returns a new array; never mutates the input.
|
|
10
|
+
*/
|
|
11
|
+
export function merge_send_back_message(tasks, parent_task_id, new_msg, build_new_task) {
|
|
12
|
+
const existing_index = tasks.findIndex(t => t.parent_task_id === parent_task_id && t.status !== 'completed');
|
|
13
|
+
if (existing_index === -1) {
|
|
14
|
+
return [...tasks, build_new_task(new_msg)];
|
|
15
|
+
}
|
|
16
|
+
const existing = tasks[existing_index];
|
|
17
|
+
const updated = {
|
|
18
|
+
...existing,
|
|
19
|
+
thread: [...existing.thread, new_msg],
|
|
20
|
+
};
|
|
21
|
+
return tasks.map((t, i) => (i === existing_index ? updated : t));
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=merge_send_back.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"merge_send_back.js","sourceRoot":"","sources":["../../../../src/components/thread_form/utils/merge_send_back.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AACH,MAAM,UAAU,uBAAuB,CACrC,KAAmB,EACnB,cAAsB,EACtB,OAAsB,EACtB,cAAkD;IAElD,MAAM,cAAc,GAAG,KAAK,CAAC,SAAS,CACpC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,cAAc,KAAK,cAAc,IAAI,CAAC,CAAC,MAAM,KAAK,WAAW,CACrE,CAAC;IAEF,IAAI,cAAc,KAAK,CAAC,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,GAAG,KAAK,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC;IAC7C,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,CAAC;IACvC,MAAM,OAAO,GAAe;QAC1B,GAAG,QAAQ;QACX,MAAM,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;KACtC,CAAC;IACF,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,cAAc,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACnE,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autofill_handler.d.ts","sourceRoot":"","sources":["../../src/lib/autofill_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,aAAa,CAAC;AAQrB,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAErD;;;OAGG;IACH,kBAAkB,EAAE;QAClB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;YAClD,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACzC,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,GAAG,OAAO,CAAC;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACnF,CAAC;IAEF;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5G;;OAEG;IACH,gBAAgB,CAAC,EAAE;QACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;QAClF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACrG,CAAC;CACH;
|
|
1
|
+
{"version":3,"file":"autofill_handler.d.ts","sourceRoot":"","sources":["../../src/lib/autofill_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,aAAa,CAAC;AAQrB,MAAM,WAAW,oBAAoB;IACnC,mDAAmD;IACnD,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAErD;;;;OAIG;IACH,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IAErD;;;OAGG;IACH,kBAAkB,EAAE;QAClB,OAAO,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;YAClD,UAAU,CAAC,EAAE,MAAM,CAAC;YACpB,SAAS,CAAC,EAAE,MAAM,CAAC;YACnB,eAAe,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;YACzC,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,GAAG,OAAO,CAAC;YAAE,OAAO,EAAE,OAAO,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAAC,KAAK,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC,CAAC;KACnF,CAAC;IAEF;;;OAGG;IACH,eAAe,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,KAAK,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;IAE5G;;OAEG;IACH,gBAAgB,CAAC,EAAE;QACjB,QAAQ,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC;YAAE,SAAS,EAAE,MAAM,CAAC;YAAC,YAAY,EAAE,MAAM,CAAA;SAAE,GAAG,IAAI,CAAC,CAAC;QAClF,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC5E,OAAO,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,CAAC;YAAC,MAAM,CAAC,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,SAAS,GAAG,MAAM,CAAA;SAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACrG,CAAC;CACH;AA6DD;;;;;;;;;;GAUG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,oBAAoB,IAKtC,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CAkIhE"}
|
|
@@ -63,19 +63,6 @@ function build_field_list_string(fields) {
|
|
|
63
63
|
return desc;
|
|
64
64
|
}).join('\n');
|
|
65
65
|
}
|
|
66
|
-
/** Check if extraction data is effectively empty (all values null/undefined/empty). */
|
|
67
|
-
function is_empty_data(data) {
|
|
68
|
-
const values = Object.values(data);
|
|
69
|
-
if (values.length === 0)
|
|
70
|
-
return true;
|
|
71
|
-
return values.every((v) => {
|
|
72
|
-
if (v === null || v === undefined || v === '')
|
|
73
|
-
return true;
|
|
74
|
-
if (Array.isArray(v) && v.length === 0)
|
|
75
|
-
return true;
|
|
76
|
-
return false;
|
|
77
|
-
});
|
|
78
|
-
}
|
|
79
66
|
// ============================================================================
|
|
80
67
|
// Route Handler Factory
|
|
81
68
|
// ============================================================================
|
|
@@ -94,26 +81,6 @@ export function create_autofill_route(options) {
|
|
|
94
81
|
const { extraction_service, metadata_service } = options;
|
|
95
82
|
const file_resolver = options.file_resolver;
|
|
96
83
|
const on_error = options.on_error ?? ((err, ctx) => console.error(`[autofill] ${ctx}:`, err));
|
|
97
|
-
/**
|
|
98
|
-
* Generate a default extraction prompt from field schemas.
|
|
99
|
-
* Used as fallback when prompt lookup fails.
|
|
100
|
-
*/
|
|
101
|
-
function generate_default_prompt(fields) {
|
|
102
|
-
const field_list = build_field_list_string(fields);
|
|
103
|
-
return `Extract the following fields from this document and return ONLY valid JSON (no markdown, no explanation).
|
|
104
|
-
|
|
105
|
-
Fields to extract:
|
|
106
|
-
${field_list}
|
|
107
|
-
|
|
108
|
-
Return a JSON object where keys are the field IDs shown in quotes above.
|
|
109
|
-
For numeric fields, return the value as a string (e.g., "1234.56").
|
|
110
|
-
For checkbox fields, return true or false.
|
|
111
|
-
For date fields, return the date in YYYY-MM-DD format.
|
|
112
|
-
For data table fields, return an array of row objects where each key matches a column ID.
|
|
113
|
-
If a field cannot be found in the document, omit it from the response.
|
|
114
|
-
|
|
115
|
-
Return ONLY the JSON object, no other text.`;
|
|
116
|
-
}
|
|
117
84
|
return async function POST(request) {
|
|
118
85
|
try {
|
|
119
86
|
const body = await request.json();
|
|
@@ -161,18 +128,15 @@ Return ONLY the JSON object, no other text.`;
|
|
|
161
128
|
// Build field_list string for prompt variable substitution
|
|
162
129
|
const field_list = build_field_list_string(fields);
|
|
163
130
|
const buffer = Buffer.from(file_b64, 'base64');
|
|
164
|
-
//
|
|
165
|
-
|
|
131
|
+
// Prompt-based extraction. The prompt MUST exist in hazo_prompts —
|
|
132
|
+
// there is no hardcoded fallback. If extraction fails or returns empty,
|
|
133
|
+
// surface that to the caller rather than silently retrying with a
|
|
134
|
+
// generated prompt (which would mask missing/broken DB prompts).
|
|
135
|
+
const result = await extraction_service.extract(buffer, mime_type, {
|
|
166
136
|
promptArea: prompt_area,
|
|
167
137
|
promptKey: prompt_key,
|
|
168
138
|
promptVariables: { field_list },
|
|
169
139
|
});
|
|
170
|
-
// Fallback: generated default prompt
|
|
171
|
-
if (!result.success || !result.data || is_empty_data(result.data)) {
|
|
172
|
-
result = await extraction_service.extract(buffer, mime_type, {
|
|
173
|
-
customPrompt: generate_default_prompt(fields),
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
140
|
// Persist extraction to hazo_files
|
|
177
141
|
if (result.success && result.data && metadata_service && body.file_id) {
|
|
178
142
|
try {
|
|
@@ -213,9 +177,6 @@ Return ONLY the JSON object, no other text.`;
|
|
|
213
177
|
prompt_area,
|
|
214
178
|
prompt_key,
|
|
215
179
|
resolved_prompt,
|
|
216
|
-
fallback_prompt_used: (!resolved_prompt)
|
|
217
|
-
? generate_default_prompt(fields)
|
|
218
|
-
: undefined,
|
|
219
180
|
},
|
|
220
181
|
});
|
|
221
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"autofill_handler.js","sourceRoot":"","sources":["../../src/lib/autofill_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,aAAa,CAAC;AAgDrB,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E;;;GAGG;AACH,SAAS,uBAAuB,CAAC,MAA6B;IAC5D,OAAO,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;QACtB,IAAI,IAAI,GAAG,MAAM,CAAC,CAAC,QAAQ,MAAM,CAAC,CAAC,KAAK,GAAG,CAAC;QAC5C,IAAI,CAAC,CAAC,WAAW;YAAE,IAAI,IAAI,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;QAChD,IAAI,CAAC,CAAC,UAAU,KAAK,SAAS;YAAE,IAAI,IAAI,kBAAkB,CAAC;QAC3D,IAAI,CAAC,CAAC,aAAa,EAAE,MAAM,EAAE,CAAC;YAC5B,IAAI,IAAI,aAAa,CAAC,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC;QACzE,CAAC;QACD,IAAI,CAAC,CAAC,cAAc,KAAK,wBAAwB;YAAE,IAAI,IAAI,eAAe,CAAC;QAC3E,IAAI,CAAC,CAAC,cAAc,KAAK,oBAAoB;YAAE,IAAI,IAAI,8BAA8B,CAAC;QAEtF,wEAAwE;QACxE,IAAI,CAAC,CAAC,cAAc,KAAK,yBAAyB,IAAI,CAAC,CAAC,YAAY,EAAE,CAAC;YACrE,MAAM,MAAM,GAAG,CAAC,CAAC,YAOhB,CAAC;YACF,IAAI,MAAM,CAAC,OAAO,EAAE,MAAM,EAAE,CAAC;gBAC3B,IAAI,IAAI,sCAAsC,CAAC;gBAC/C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACjC,IAAI,QAAQ,GAAG,YAAY,GAAG,CAAC,EAAE,MAAM,GAAG,CAAC,KAAK,GAAG,CAAC;oBACpD,IAAI,GAAG,CAAC,UAAU,KAAK,SAAS,IAAI,GAAG,CAAC,UAAU,KAAK,UAAU,EAAE,CAAC;wBAClE,QAAQ,IAAI,WAAW,CAAC;wBACxB,IAAI,GAAG,CAAC,WAAW,EAAE,cAAc,KAAK,SAAS,EAAE,CAAC;4BAClD,QAAQ,IAAI,KAAK,GAAG,CAAC,WAAW,CAAC,cAAc,kBAAkB,CAAC;wBACpE,CAAC;oBACH,CAAC;yBAAM,IAAI,GAAG,CAAC,UAAU,KAAK,UAAU,IAAI,GAAG,CAAC,UAAU,KAAK,aAAa,EAAE,CAAC;wBAC7E,QAAQ,IAAI,KAAK,GAAG,CAAC,UAAU,EAAE,CAAC;oBACpC,CAAC;yBAAM,CAAC;wBACN,QAAQ,IAAI,QAAQ,CAAC;oBACvB,CAAC;oBACD,IAAI,GAAG,CAAC,WAAW,EAAE,QAAQ;wBAAE,QAAQ,IAAI,aAAa,CAAC;oBACzD,IAAI,IAAI,QAAQ,CAAC;gBACnB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,IAAI,IAAI,yBAAyB,CAAC;YACpC,CAAC;QACH,CAAC;QAED,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAChB,CAAC;AAED
|
|
1
|
+
{"version":3,"file":"autofill_handler.js","sourceRoot":"","sources":["../../src/lib/autofill_handler.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side classification route handler factory.
|
|
3
|
+
*
|
|
4
|
+
* Creates a POST handler that classifies uploaded documents via LLM analysis,
|
|
5
|
+
* returning document type, tags, confidence, and optional metadata.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { create_classification_route } from 'hazo_collab_forms/lib';
|
|
9
|
+
* export const POST = create_classification_route({ hazo_connect_adapter: getHazoConnect() });
|
|
10
|
+
*
|
|
11
|
+
* Uses standard Web API Request/Response for portability.
|
|
12
|
+
* hazo_llm_api is dynamically imported so it's only required at runtime.
|
|
13
|
+
*/
|
|
14
|
+
import 'server-only';
|
|
15
|
+
export interface ClassificationRouteOptions {
|
|
16
|
+
/** Path to SQLite database for prompt library */
|
|
17
|
+
sqlite_path?: string;
|
|
18
|
+
/** Pre-configured hazo_connect adapter (takes precedence over sqlite_path) */
|
|
19
|
+
hazo_connect_adapter?: unknown;
|
|
20
|
+
/** Error handler */
|
|
21
|
+
on_error?: (err: unknown, ctx: string) => void;
|
|
22
|
+
/**
|
|
23
|
+
* prompt_area for the classification prompt lookup. Default: 'document'.
|
|
24
|
+
*/
|
|
25
|
+
prompt_area?: string;
|
|
26
|
+
/**
|
|
27
|
+
* prompt_key for the classification prompt lookup. Default: 'classification'.
|
|
28
|
+
*/
|
|
29
|
+
prompt_key?: string;
|
|
30
|
+
}
|
|
31
|
+
export interface ClassificationDocumentType {
|
|
32
|
+
type_id: string;
|
|
33
|
+
type_label: string;
|
|
34
|
+
description?: string;
|
|
35
|
+
}
|
|
36
|
+
export interface ClassificationAllowedTag {
|
|
37
|
+
tag_id: string;
|
|
38
|
+
tag_label: string;
|
|
39
|
+
description?: string;
|
|
40
|
+
}
|
|
41
|
+
export interface ClassificationRequest {
|
|
42
|
+
file_b64: string;
|
|
43
|
+
file_name: string;
|
|
44
|
+
mime_type: string;
|
|
45
|
+
/**
|
|
46
|
+
* Optional allow-list of document types. When provided, the LLM is
|
|
47
|
+
* instructed to choose exactly one type_id from this list (or 'unknown'
|
|
48
|
+
* if nothing fits). Substituted into the prompt as {{document_types}} and
|
|
49
|
+
* {{document_type}} (the prompt uses both forms).
|
|
50
|
+
*/
|
|
51
|
+
document_types?: ClassificationDocumentType[];
|
|
52
|
+
/**
|
|
53
|
+
* Optional allow-list of classification tags. When provided, the LLM is
|
|
54
|
+
* instructed to use only these tag_ids. Rendered into {{tag_list}}.
|
|
55
|
+
*/
|
|
56
|
+
available_tags?: ClassificationAllowedTag[];
|
|
57
|
+
/**
|
|
58
|
+
* Optional pre-rendered tag-list string. Takes precedence over
|
|
59
|
+
* available_tags if both are supplied.
|
|
60
|
+
*/
|
|
61
|
+
tag_list?: string;
|
|
62
|
+
/**
|
|
63
|
+
* Optional free-form client text to aid classification. Substituted into
|
|
64
|
+
* the prompt as {{client_text}}.
|
|
65
|
+
*/
|
|
66
|
+
client_text?: string;
|
|
67
|
+
}
|
|
68
|
+
export interface ClassificationTagReason {
|
|
69
|
+
name: string;
|
|
70
|
+
reason: string;
|
|
71
|
+
}
|
|
72
|
+
export interface ClassificationResponse {
|
|
73
|
+
success: boolean;
|
|
74
|
+
document_type?: string;
|
|
75
|
+
tags?: string[];
|
|
76
|
+
/** Per-tag reasons from the LLM, preserved for debug/review. */
|
|
77
|
+
tag_reasons?: ClassificationTagReason[];
|
|
78
|
+
confidence?: number;
|
|
79
|
+
document_date?: string;
|
|
80
|
+
document_nature?: string;
|
|
81
|
+
/**
|
|
82
|
+
* Structured monetary fields lifted out of the document during classification —
|
|
83
|
+
* populated when the classification prompt asks for them (e.g. for invoice or
|
|
84
|
+
* receipt doc types). Used by the aggregate sum_match resolver to compare
|
|
85
|
+
* receipt totals against an expected amount.
|
|
86
|
+
*/
|
|
87
|
+
extracted_fields?: {
|
|
88
|
+
total?: number;
|
|
89
|
+
date?: string;
|
|
90
|
+
vendor?: string;
|
|
91
|
+
property_address?: string;
|
|
92
|
+
};
|
|
93
|
+
error?: string;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Creates a POST route handler for document classification.
|
|
97
|
+
*
|
|
98
|
+
* The handler:
|
|
99
|
+
* 1. Validates the request payload
|
|
100
|
+
* 2. Sends the document to an LLM for classification
|
|
101
|
+
* 3. Parses the response into structured classification data
|
|
102
|
+
* 4. Returns the classification result
|
|
103
|
+
*/
|
|
104
|
+
export declare function create_classification_route(options?: ClassificationRouteOptions): (request: Request) => Promise<Response>;
|
|
105
|
+
//# sourceMappingURL=classification_handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"classification_handler.d.ts","sourceRoot":"","sources":["../../src/lib/classification_handler.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,aAAa,CAAC;AAQrB,MAAM,WAAW,0BAA0B;IACzC,iDAAiD;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8EAA8E;IAC9E,oBAAoB,CAAC,EAAE,OAAO,CAAC;IAC/B,oBAAoB;IACpB,QAAQ,CAAC,EAAE,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/C;;OAEG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB;;OAEG;IACH,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,0BAA0B;IACzC,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,0BAA0B,EAAE,CAAC;IAC9C;;;OAGG;IACH,cAAc,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC5C;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,OAAO,CAAC;IACjB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;IAChB,gEAAgE;IAChE,WAAW,CAAC,EAAE,uBAAuB,EAAE,CAAC;IACxC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB;;;;;OAKG;IACH,gBAAgB,CAAC,EAAE;QACjB,KAAK,CAAC,EAAE,MAAM,CAAC;QACf,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,gBAAgB,CAAC,EAAE,MAAM,CAAC;KAC3B,CAAC;IACF,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAuLD;;;;;;;;GAQG;AACH,wBAAgB,2BAA2B,CAAC,OAAO,CAAC,EAAE,0BAA0B,IAyDnD,SAAS,OAAO,KAAG,OAAO,CAAC,QAAQ,CAAC,CA2JhE"}
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Server-side classification route handler factory.
|
|
3
|
+
*
|
|
4
|
+
* Creates a POST handler that classifies uploaded documents via LLM analysis,
|
|
5
|
+
* returning document type, tags, confidence, and optional metadata.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* import { create_classification_route } from 'hazo_collab_forms/lib';
|
|
9
|
+
* export const POST = create_classification_route({ hazo_connect_adapter: getHazoConnect() });
|
|
10
|
+
*
|
|
11
|
+
* Uses standard Web API Request/Response for portability.
|
|
12
|
+
* hazo_llm_api is dynamically imported so it's only required at runtime.
|
|
13
|
+
*/
|
|
14
|
+
import 'server-only';
|
|
15
|
+
import { get_config } from './config.js';
|
|
16
|
+
// ============================================================================
|
|
17
|
+
// Helpers
|
|
18
|
+
// ============================================================================
|
|
19
|
+
function is_image_mime(mime_type) {
|
|
20
|
+
return mime_type.startsWith('image/');
|
|
21
|
+
}
|
|
22
|
+
function is_document_mime(mime_type) {
|
|
23
|
+
return mime_type === 'application/pdf';
|
|
24
|
+
}
|
|
25
|
+
/** Render the document-types list as a multi-line string for prompt substitution. */
|
|
26
|
+
function render_document_types(list) {
|
|
27
|
+
if (!list || list.length === 0) {
|
|
28
|
+
return '(none — respond with "unknown" for document_type)';
|
|
29
|
+
}
|
|
30
|
+
return list.map(dt => {
|
|
31
|
+
const label = dt.type_label ?? dt.type_id;
|
|
32
|
+
const desc = dt.description ? ` — ${dt.description}` : '';
|
|
33
|
+
return `- ${dt.type_id}: ${label}${desc}`;
|
|
34
|
+
}).join('\n');
|
|
35
|
+
}
|
|
36
|
+
/** Render the allowed-tags list as a multi-line string for prompt substitution. */
|
|
37
|
+
function render_tag_list(list) {
|
|
38
|
+
if (!list || list.length === 0) {
|
|
39
|
+
return '(no allow-list — return an empty tags array)';
|
|
40
|
+
}
|
|
41
|
+
return list.map(t => {
|
|
42
|
+
const label = t.tag_label ?? t.tag_id;
|
|
43
|
+
const desc = t.description ? ` — ${t.description}` : '';
|
|
44
|
+
return `- ${t.tag_id}: ${label}${desc}`;
|
|
45
|
+
}).join('\n');
|
|
46
|
+
}
|
|
47
|
+
/** Substitute {{var}} placeholders. Unknown variables are left as-is. */
|
|
48
|
+
function substitute_variables(prompt, variables) {
|
|
49
|
+
return prompt.replace(/\{\{(\w+)\}\}/g, (_, key) => variables[key] ?? `{{${key}}}`);
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Parse LLM response for classification results.
|
|
53
|
+
*
|
|
54
|
+
* The prompt asks for an array (one entry per file); for single-file
|
|
55
|
+
* classification we take the first entry.
|
|
56
|
+
*/
|
|
57
|
+
function parse_classification_response(text) {
|
|
58
|
+
const cleaned = text.trim();
|
|
59
|
+
const candidates = [cleaned];
|
|
60
|
+
const fence_match = cleaned.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
61
|
+
if (fence_match)
|
|
62
|
+
candidates.push(fence_match[1].trim());
|
|
63
|
+
const array_match = cleaned.match(/\[[\s\S]*\]/);
|
|
64
|
+
if (array_match)
|
|
65
|
+
candidates.push(array_match[0]);
|
|
66
|
+
const object_match = cleaned.match(/\{[\s\S]*\}/);
|
|
67
|
+
if (object_match)
|
|
68
|
+
candidates.push(object_match[0]);
|
|
69
|
+
for (const candidate of candidates) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(candidate);
|
|
72
|
+
const item = pick_first_entry(parsed);
|
|
73
|
+
if (item)
|
|
74
|
+
return extract_classification_fields(item);
|
|
75
|
+
}
|
|
76
|
+
catch {
|
|
77
|
+
// Try next candidate
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
document_type: 'unknown',
|
|
82
|
+
tags: [],
|
|
83
|
+
tag_reasons: [],
|
|
84
|
+
confidence: 0,
|
|
85
|
+
document_nature: cleaned.slice(0, 200),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
function pick_first_entry(parsed) {
|
|
89
|
+
if (Array.isArray(parsed)) {
|
|
90
|
+
return parsed[0] ?? null;
|
|
91
|
+
}
|
|
92
|
+
if (parsed && typeof parsed === 'object') {
|
|
93
|
+
return parsed;
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
function extract_classification_fields(parsed) {
|
|
98
|
+
const raw_tags = Array.isArray(parsed.tags) ? parsed.tags : [];
|
|
99
|
+
const tags = [];
|
|
100
|
+
const tag_reasons = [];
|
|
101
|
+
for (const t of raw_tags) {
|
|
102
|
+
if (typeof t === 'string') {
|
|
103
|
+
tags.push(t);
|
|
104
|
+
}
|
|
105
|
+
else if (t && typeof t === 'object') {
|
|
106
|
+
const name = String(t.name ?? '').trim();
|
|
107
|
+
if (!name)
|
|
108
|
+
continue;
|
|
109
|
+
tags.push(name);
|
|
110
|
+
const reason = t.reason;
|
|
111
|
+
if (typeof reason === 'string' && reason.trim()) {
|
|
112
|
+
tag_reasons.push({ name, reason: reason.trim() });
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
// document_type may come through as a string or a single-element array.
|
|
117
|
+
let document_type = 'unknown';
|
|
118
|
+
const raw_dt = parsed.document_type;
|
|
119
|
+
if (typeof raw_dt === 'string' && raw_dt.trim()) {
|
|
120
|
+
document_type = raw_dt.trim();
|
|
121
|
+
}
|
|
122
|
+
else if (Array.isArray(raw_dt) && raw_dt.length > 0 && typeof raw_dt[0] === 'string') {
|
|
123
|
+
document_type = raw_dt[0].trim() || 'unknown';
|
|
124
|
+
}
|
|
125
|
+
// confidence as decimal; if the LLM slips and returns a percent (>1.5), normalise.
|
|
126
|
+
let confidence = 0;
|
|
127
|
+
if (typeof parsed.confidence === 'number' && Number.isFinite(parsed.confidence)) {
|
|
128
|
+
confidence = parsed.confidence > 1.5 ? parsed.confidence / 100 : parsed.confidence;
|
|
129
|
+
if (confidence < 0)
|
|
130
|
+
confidence = 0;
|
|
131
|
+
if (confidence > 1)
|
|
132
|
+
confidence = 1;
|
|
133
|
+
}
|
|
134
|
+
const document_date = typeof parsed.document_date === 'string' && parsed.document_date.trim()
|
|
135
|
+
? parsed.document_date.trim()
|
|
136
|
+
: undefined;
|
|
137
|
+
const document_nature = typeof parsed.document_nature === 'string' && parsed.document_nature.trim()
|
|
138
|
+
? parsed.document_nature.trim()
|
|
139
|
+
: undefined;
|
|
140
|
+
// Structured fields — accept either a nested `extracted_fields` object or
|
|
141
|
+
// top-level keys for backward compatibility. Growing set: add new keys here
|
|
142
|
+
// as the prompt evolves. Missing keys stay undefined so downstream
|
|
143
|
+
// resolvers can detect "not extracted" cleanly.
|
|
144
|
+
let extracted_fields;
|
|
145
|
+
const ef_source = (parsed.extracted_fields && typeof parsed.extracted_fields === 'object')
|
|
146
|
+
? parsed.extracted_fields
|
|
147
|
+
: parsed;
|
|
148
|
+
const total_raw = ef_source.total ?? ef_source.amount ?? ef_source.invoice_total;
|
|
149
|
+
const date_raw = ef_source.invoice_date ?? ef_source.receipt_date ?? (parsed.extracted_fields ? ef_source.date : undefined);
|
|
150
|
+
const vendor_raw = ef_source.vendor ?? ef_source.supplier ?? ef_source.payee;
|
|
151
|
+
const address_raw = ef_source.property_address ?? ef_source.address ?? ef_source.site_address;
|
|
152
|
+
let total;
|
|
153
|
+
if (typeof total_raw === 'number' && Number.isFinite(total_raw)) {
|
|
154
|
+
total = total_raw;
|
|
155
|
+
}
|
|
156
|
+
else if (typeof total_raw === 'string' && total_raw.trim()) {
|
|
157
|
+
const n = parseFloat(total_raw.replace(/[^\d.\-]/g, ''));
|
|
158
|
+
if (Number.isFinite(n))
|
|
159
|
+
total = n;
|
|
160
|
+
}
|
|
161
|
+
const date_str = typeof date_raw === 'string' && date_raw.trim() ? date_raw.trim() : undefined;
|
|
162
|
+
const vendor_str = typeof vendor_raw === 'string' && vendor_raw.trim() ? vendor_raw.trim() : undefined;
|
|
163
|
+
const address_str = typeof address_raw === 'string' && address_raw.trim() ? address_raw.trim() : undefined;
|
|
164
|
+
if (total !== undefined || date_str || vendor_str || address_str) {
|
|
165
|
+
extracted_fields = {
|
|
166
|
+
...(total !== undefined ? { total } : {}),
|
|
167
|
+
...(date_str ? { date: date_str } : {}),
|
|
168
|
+
...(vendor_str ? { vendor: vendor_str } : {}),
|
|
169
|
+
...(address_str ? { property_address: address_str } : {}),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return { document_type, tags, tag_reasons, confidence, document_date, document_nature, extracted_fields };
|
|
173
|
+
}
|
|
174
|
+
// ============================================================================
|
|
175
|
+
// Route Handler Factory
|
|
176
|
+
// ============================================================================
|
|
177
|
+
/**
|
|
178
|
+
* Creates a POST route handler for document classification.
|
|
179
|
+
*
|
|
180
|
+
* The handler:
|
|
181
|
+
* 1. Validates the request payload
|
|
182
|
+
* 2. Sends the document to an LLM for classification
|
|
183
|
+
* 3. Parses the response into structured classification data
|
|
184
|
+
* 4. Returns the classification result
|
|
185
|
+
*/
|
|
186
|
+
export function create_classification_route(options) {
|
|
187
|
+
const sqlite_path = options?.sqlite_path ?? get_config('hazo_connect', 'prompt_sqlite_path') ?? 'data/prompt_library.sqlite';
|
|
188
|
+
const hazo_connect_adapter = options?.hazo_connect_adapter;
|
|
189
|
+
const on_error = options?.on_error ?? ((err, ctx) => console.error(`[classification] ${ctx}:`, err));
|
|
190
|
+
const prompt_area = options?.prompt_area ?? 'document';
|
|
191
|
+
const prompt_key = options?.prompt_key ?? 'classification';
|
|
192
|
+
// Cache the dynamic import
|
|
193
|
+
let llm_api_module = null;
|
|
194
|
+
let init_promise = null;
|
|
195
|
+
async function get_llm_api() {
|
|
196
|
+
if (!llm_api_module) {
|
|
197
|
+
try {
|
|
198
|
+
const module_id = 'hazo_llm_api/server';
|
|
199
|
+
llm_api_module = await import(/* webpackIgnore: true */ module_id);
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
throw new Error('hazo_llm_api is required for classification. Install it: npm install hazo_llm_api');
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
return llm_api_module;
|
|
206
|
+
}
|
|
207
|
+
async function ensure_initialized() {
|
|
208
|
+
const api = await get_llm_api();
|
|
209
|
+
if (!api.is_initialized()) {
|
|
210
|
+
if (!init_promise) {
|
|
211
|
+
const init_config = hazo_connect_adapter
|
|
212
|
+
? { hazo_connect_adapter }
|
|
213
|
+
: { sqlite_path };
|
|
214
|
+
init_promise = api.initialize_llm_api(init_config).then(() => { });
|
|
215
|
+
}
|
|
216
|
+
await init_promise;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return async function POST(request) {
|
|
220
|
+
try {
|
|
221
|
+
const body = await request.json();
|
|
222
|
+
const { file_b64, file_name, mime_type, document_types, available_tags, tag_list, client_text } = body;
|
|
223
|
+
// Validate required fields
|
|
224
|
+
if (!file_b64 || !file_name || !mime_type) {
|
|
225
|
+
return Response.json({ success: false, error: 'file_b64, file_name, and mime_type are required' }, { status: 400 });
|
|
226
|
+
}
|
|
227
|
+
// Initialize LLM API
|
|
228
|
+
await ensure_initialized();
|
|
229
|
+
const api = await get_llm_api();
|
|
230
|
+
// Load prompt from hazo_prompts. No hardcoded fallback — error loudly so
|
|
231
|
+
// missing prompts get spotted in dev rather than silently using stale code.
|
|
232
|
+
const connect = api.get_hazo_connect?.() ?? null;
|
|
233
|
+
if (!connect) {
|
|
234
|
+
return Response.json({ success: false, error: 'hazo_llm_api hazo_connect is not available — cannot load prompts from hazo_prompts' }, { status: 500 });
|
|
235
|
+
}
|
|
236
|
+
const lookup = await connect.get_by_area_key(prompt_area, prompt_key);
|
|
237
|
+
const db_prompt = lookup.success ? lookup.data?.prompt_text_full : undefined;
|
|
238
|
+
if (!db_prompt) {
|
|
239
|
+
return Response.json({ success: false, error: `Prompt ${prompt_area}/${prompt_key} not found in hazo_prompts. Seed it before calling this route.` }, { status: 500 });
|
|
240
|
+
}
|
|
241
|
+
// The prompt uses a mix of {{document_types}} and {{document_type}} to
|
|
242
|
+
// mean the doc-types list; map both to the same rendered list.
|
|
243
|
+
const doc_types_text = render_document_types(document_types);
|
|
244
|
+
const tags_text = tag_list ?? render_tag_list(available_tags);
|
|
245
|
+
const prompt_text = substitute_variables(db_prompt, {
|
|
246
|
+
document_types: doc_types_text,
|
|
247
|
+
document_type: doc_types_text,
|
|
248
|
+
tag_list: tags_text,
|
|
249
|
+
client_text: client_text ?? '(none)',
|
|
250
|
+
// Single-file flow: synthesise a minimal file_list. The LLM still
|
|
251
|
+
// inspects the file contents; file_id is just a correlation key.
|
|
252
|
+
file_list: `- ${file_name}`,
|
|
253
|
+
});
|
|
254
|
+
// Call LLM based on mime type
|
|
255
|
+
let llm_response;
|
|
256
|
+
if (is_image_mime(mime_type)) {
|
|
257
|
+
llm_response = await api.hazo_llm_image_text({
|
|
258
|
+
prompt: prompt_text,
|
|
259
|
+
image_b64: file_b64,
|
|
260
|
+
image_mime_type: mime_type,
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
else if (is_document_mime(mime_type)) {
|
|
264
|
+
llm_response = await api.hazo_llm_document_text({
|
|
265
|
+
prompt: prompt_text,
|
|
266
|
+
document_b64: file_b64,
|
|
267
|
+
document_mime_type: mime_type,
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
else {
|
|
271
|
+
// Fallback: treat as image
|
|
272
|
+
llm_response = await api.hazo_llm_image_text({
|
|
273
|
+
prompt: prompt_text,
|
|
274
|
+
image_b64: file_b64,
|
|
275
|
+
image_mime_type: mime_type,
|
|
276
|
+
});
|
|
277
|
+
}
|
|
278
|
+
if (!llm_response.success || !llm_response.text) {
|
|
279
|
+
return Response.json({ success: false, error: llm_response.error ?? 'LLM call failed' }, { status: 500 });
|
|
280
|
+
}
|
|
281
|
+
// Parse classification result
|
|
282
|
+
const result = parse_classification_response(llm_response.text);
|
|
283
|
+
// Enforce the document_types allow-list. The prompt instructs the LLM to
|
|
284
|
+
// pick from the list, but LLMs occasionally invent their own label (e.g.
|
|
285
|
+
// "property_statement" when only "rental_statement" is configured). When
|
|
286
|
+
// that happens, fall back to 'unknown' so downstream validation doesn't
|
|
287
|
+
// key off a type the rest of the system has never heard of.
|
|
288
|
+
let document_type = result.document_type;
|
|
289
|
+
if (document_types && document_types.length > 0 && document_type !== 'unknown') {
|
|
290
|
+
const allowed = new Set(document_types.map(d => d.type_id));
|
|
291
|
+
if (!allowed.has(document_type)) {
|
|
292
|
+
on_error(new Error(`LLM returned document_type "${document_type}" which is not in the configured allow-list (${[...allowed].join(', ')}). Falling back to "unknown".`), 'document_type not in allow-list');
|
|
293
|
+
document_type = 'unknown';
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
// Defensive warning — if the LLM came back "unknown" AND we never gave
|
|
297
|
+
// it an allow-list to choose from, that's almost certainly the root
|
|
298
|
+
// cause. Surface a clear hint so future debugging doesn't repeat the
|
|
299
|
+
// hazo_app_config-empty rabbit hole.
|
|
300
|
+
if (document_type === 'unknown' &&
|
|
301
|
+
result.confidence === 0 &&
|
|
302
|
+
(!document_types || document_types.length === 0)) {
|
|
303
|
+
const msg = `[classification_handler] document_type='unknown' with confidence=0 — and no document_types allow-list was passed. ` +
|
|
304
|
+
`Likely cause: the consumer didn't provide \`available_document_types\` (or the underlying config table is empty). ` +
|
|
305
|
+
`Populate the doc-types config (e.g. via the doc-type-editor page or a seed script) so the LLM has a list to choose from.`;
|
|
306
|
+
console.warn(msg);
|
|
307
|
+
on_error(new Error(msg), 'empty document_types allow-list');
|
|
308
|
+
}
|
|
309
|
+
// Enforce the tags allow-list when provided.
|
|
310
|
+
let tags = result.tags;
|
|
311
|
+
let tag_reasons = result.tag_reasons;
|
|
312
|
+
if (available_tags && available_tags.length > 0) {
|
|
313
|
+
const allowed_tags = new Set(available_tags.map(t => t.tag_id));
|
|
314
|
+
tags = tags.filter(t => allowed_tags.has(t));
|
|
315
|
+
tag_reasons = tag_reasons.filter(r => allowed_tags.has(r.name));
|
|
316
|
+
}
|
|
317
|
+
const response = {
|
|
318
|
+
success: true,
|
|
319
|
+
document_type,
|
|
320
|
+
tags,
|
|
321
|
+
tag_reasons,
|
|
322
|
+
confidence: result.confidence,
|
|
323
|
+
document_date: result.document_date,
|
|
324
|
+
document_nature: result.document_nature,
|
|
325
|
+
...(result.extracted_fields ? { extracted_fields: result.extracted_fields } : {}),
|
|
326
|
+
};
|
|
327
|
+
// Debug: when extraction came back empty for an invoice/receipt-type doc,
|
|
328
|
+
// include the raw LLM text on the response so the consumer can inspect it
|
|
329
|
+
// in the Debug JSON dialog. Stripped on extraction success to keep
|
|
330
|
+
// payloads small.
|
|
331
|
+
if (!result.extracted_fields && (document_type === 'invoice' || document_type === 'receipt' || document_type === 'tax_invoice')) {
|
|
332
|
+
response._debug_llm_text = llm_response.text;
|
|
333
|
+
}
|
|
334
|
+
return Response.json(response);
|
|
335
|
+
}
|
|
336
|
+
catch (error) {
|
|
337
|
+
on_error(error, 'unhandled error');
|
|
338
|
+
return Response.json({ success: false, error: error instanceof Error ? error.message : 'Unknown error' }, { status: 500 });
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
}
|
|
342
|
+
//# sourceMappingURL=classification_handler.js.map
|