claude-smart 0.2.42 → 0.2.43
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/.claude-plugin/marketplace.json +3 -3
- package/README.md +1 -1
- package/package.json +9 -3
- package/plugin/.claude-plugin/plugin.json +9 -3
- package/plugin/.codex-plugin/plugin.json +1 -1
- package/plugin/README.md +2 -2
- package/plugin/pyproject.toml +2 -2
- package/plugin/scripts/_lib.sh +91 -0
- package/plugin/scripts/backend-service.sh +1 -0
- package/plugin/scripts/cli.sh +1 -0
- package/plugin/scripts/codex-hook.js +72 -4
- package/plugin/scripts/dashboard-build.sh +1 -0
- package/plugin/scripts/dashboard-service.sh +1 -0
- package/plugin/scripts/hook_entry.sh +1 -0
- package/plugin/scripts/smart-install.sh +1 -0
- package/plugin/src/claude_smart/context_format.py +11 -12
- package/plugin/src/claude_smart/cs_cite.py +26 -12
- package/plugin/src/claude_smart/ids.py +13 -5
- package/plugin/uv.lock +1 -1
- package/plugin/vendor/reflexio/.env.example +53 -0
- package/plugin/vendor/reflexio/LICENSE +201 -0
- package/plugin/vendor/reflexio/README.md +338 -0
- package/plugin/vendor/reflexio/pyproject.toml +271 -0
- package/plugin/vendor/reflexio/reflexio/README.md +184 -0
- package/plugin/vendor/reflexio/reflexio/__init__.py +166 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/README.md +109 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/backends.py +175 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/bench.py +642 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/embed_cache.py +330 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/report.py +317 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/report.md +43 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/results/results.json +4478 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/scenarios.py +134 -0
- package/plugin/vendor/reflexio/reflexio/benchmarks/retrieval_latency/seed.py +255 -0
- package/plugin/vendor/reflexio/reflexio/cli/README.md +287 -0
- package/plugin/vendor/reflexio/reflexio/cli/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/__main__.py +56 -0
- package/plugin/vendor/reflexio/reflexio/cli/_client.py +86 -0
- package/plugin/vendor/reflexio/reflexio/cli/app.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/bootstrap_config.py +266 -0
- package/plugin/vendor/reflexio/reflexio/cli/codex_auth.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/admin_cmd.py +65 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/agent_playbooks.py +503 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/api.py +114 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/auth.py +109 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/config_cmd.py +511 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/doctor.py +127 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/embeddings.py +53 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/interactions.py +478 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/profiles.py +303 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/services.py +289 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/setup_cmd.py +961 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/shortcuts.py +285 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/status_cmd.py +143 -0
- package/plugin/vendor/reflexio/reflexio/cli/commands/user_playbooks.py +373 -0
- package/plugin/vendor/reflexio/reflexio/cli/env_loader.py +284 -0
- package/plugin/vendor/reflexio/reflexio/cli/errors.py +217 -0
- package/plugin/vendor/reflexio/reflexio/cli/log_format.py +247 -0
- package/plugin/vendor/reflexio/reflexio/cli/output.py +867 -0
- package/plugin/vendor/reflexio/reflexio/cli/paths.py +41 -0
- package/plugin/vendor/reflexio/reflexio/cli/run_services.py +391 -0
- package/plugin/vendor/reflexio/reflexio/cli/state.py +204 -0
- package/plugin/vendor/reflexio/reflexio/cli/stop_services.py +96 -0
- package/plugin/vendor/reflexio/reflexio/cli/utils.py +329 -0
- package/plugin/vendor/reflexio/reflexio/client/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/client/cache.py +150 -0
- package/plugin/vendor/reflexio/reflexio/client/client.py +2613 -0
- package/plugin/vendor/reflexio/reflexio/defaults.py +23 -0
- package/plugin/vendor/reflexio/reflexio/integrations/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/.clawhubignore +7 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/README.md +274 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/TESTING.md +517 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/hook/handler.js +473 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package-lock.json +2156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/handler.ts +241 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/hook/setup.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/index.ts +130 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/publish.ts +113 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/search.ts +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/server.ts +103 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/sqlite-buffer.ts +156 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/lib/user-id.ts +134 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/openclaw.plugin.json +41 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/package.json +17 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/rules/reflexio.md +24 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/plugin/skills/reflexio/SKILL.md +48 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/publish_clawhub.sh +278 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/references/HOOK.md +164 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/install.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/scripts/uninstall.sh +35 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/publish.test.ts +27 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/search.test.ts +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/server.test.ts +42 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/setup.test.ts +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/sqlite-buffer.test.ts +91 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tests/user-id.test.ts +50 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw/vitest.config.ts +13 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/README.md +120 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/TESTING.md +168 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package-lock.json +1657 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/package.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/HEARTBEAT.md +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/README.md +84 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/_meta.json +6 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/agents/reflexio-extractor.md +45 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/handler.ts +214 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/hook/setup.ts +55 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/index.ts +327 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/consolidate.ts +233 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/dedup.ts +80 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/io.ts +155 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/openclaw-cli.ts +67 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/search.ts +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-playbook.ts +76 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/lib/write-profile.ts +79 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/openclaw.plugin.json +46 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/package.json +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/README.md +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/full_consolidation.md +56 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/playbook_extraction.md +217 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/prompts/profile_extraction.md +132 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-consolidate/SKILL.md +33 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/plugin/skills/reflexio-embedded/SKILL.md +194 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/HOOK.md +18 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/architecture.md +49 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/comparison.md +31 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/future-work.md +47 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/references/porting-notes.md +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/install.sh +52 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/scripts/uninstall.sh +36 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/consolidate.test.ts +135 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/dedup.test.ts +104 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/io.test.ts +175 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/search.test.ts +66 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/smoke-test.ts +140 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-playbook.test.ts +93 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tests/write-profile.test.ts +174 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/tsconfig.json +16 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/types/openclaw.d.ts +230 -0
- package/plugin/vendor/reflexio/reflexio/integrations/openclaw-embedded/vitest.config.ts +7 -0
- package/plugin/vendor/reflexio/reflexio/lib/__init__.py +23 -0
- package/plugin/vendor/reflexio/reflexio/lib/_agent_playbook.py +310 -0
- package/plugin/vendor/reflexio/reflexio/lib/_base.py +225 -0
- package/plugin/vendor/reflexio/reflexio/lib/_config.py +83 -0
- package/plugin/vendor/reflexio/reflexio/lib/_dashboard.py +266 -0
- package/plugin/vendor/reflexio/reflexio/lib/_generation.py +176 -0
- package/plugin/vendor/reflexio/reflexio/lib/_interactions.py +334 -0
- package/plugin/vendor/reflexio/reflexio/lib/_operations.py +153 -0
- package/plugin/vendor/reflexio/reflexio/lib/_profiles.py +545 -0
- package/plugin/vendor/reflexio/reflexio/lib/_reflection.py +52 -0
- package/plugin/vendor/reflexio/reflexio/lib/_search.py +167 -0
- package/plugin/vendor/reflexio/reflexio/lib/_storage_labels.py +103 -0
- package/plugin/vendor/reflexio/reflexio/lib/_user_playbook.py +288 -0
- package/plugin/vendor/reflexio/reflexio/lib/reflexio_lib.py +27 -0
- package/plugin/vendor/reflexio/reflexio/models/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/braintrust_schema.py +141 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/common.py +41 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/entities.py +1103 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/domain/enums.py +63 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/eval_overview_schema.py +487 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/internal_schema.py +28 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/pending_tool_call_schema.py +83 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/retriever_schema.py +766 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/service_schemas.py +9 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/stall_state_schema.py +32 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/__init__.py +3 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/converters.py +177 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/entities.py +129 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/ui/enums.py +25 -0
- package/plugin/vendor/reflexio/reflexio/models/api_schema/validators.py +280 -0
- package/plugin/vendor/reflexio/reflexio/models/config_schema.py +908 -0
- package/plugin/vendor/reflexio/reflexio/models/py.typed +0 -0
- package/plugin/vendor/reflexio/reflexio/server/OVERVIEW.md +90 -0
- package/plugin/vendor/reflexio/reflexio/server/README.md +616 -0
- package/plugin/vendor/reflexio/reflexio/server/__init__.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/__main__.py +132 -0
- package/plugin/vendor/reflexio/reflexio/server/_auth.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/api.py +2714 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/account_api.py +143 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/health_api.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/pending_tool_call_api.py +572 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/precondition_checks.py +66 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/publisher_api.py +540 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/request_context.py +50 -0
- package/plugin/vendor/reflexio/reflexio/server/api_endpoints/stall_state_api.py +100 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/__init__.py +15 -0
- package/plugin/vendor/reflexio/reflexio/server/cache/reflexio_cache.py +208 -0
- package/plugin/vendor/reflexio/reflexio/server/correlation.py +46 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/__init__.py +30 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/embedding_service.py +110 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/image_utils.py +55 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/litellm_client.py +1595 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/llm_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/model_defaults.py +469 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_provider.py +1122 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/claude_code_stream_parser.py +197 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/embedding_service_provider.py +210 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/local_embedding_provider.py +213 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/providers/nomic_embedding_provider.py +255 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/__init__.py +6 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/cross_encoder_reranker.py +177 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/rerank/llm_reranker.py +148 -0
- package/plugin/vendor/reflexio/reflexio/server/llm/tools.py +699 -0
- package/plugin/vendor/reflexio/reflexio/server/operation_limiter.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/_dispatchers.py +54 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/README.md +121 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation/v1.0.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/agent_success_evaluation_with_comparison/v1.0.0.prompt.md +76 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/answer_synthesis/v1.5.2.prompt.md +88 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/compress_session_for_query/v1.3.0.prompt.md +31 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/document_expansion/v1.0.0.prompt.md +20 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.0.0.prompt.md +53 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.1.0.prompt.md +57 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.2.0.prompt.md +68 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.3.0.prompt.md +70 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.4.0.prompt.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.5.0.prompt.md +82 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/memory_reflection/v1.6.0.prompt.md +83 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.1.0.prompt.md +193 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_aggregation/v2.2.0.prompt.md +206 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0-deprecated.prompt.md +66 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v1.1.0.prompt.md +46 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0-deprecated.prompt.md +64 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.0.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.1.0.prompt.md +39 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.2.0.prompt.md +47 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_consolidation/v2.3.0.prompt.md +58 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.0.2.prompt.md +254 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.1.0.prompt.md +274 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context/v4.2.0.prompt.md +279 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v1.0.0.prompt.md +73 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v2.0.0.prompt.md +86 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.0.0.prompt.md +97 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.1.0.prompt.md +119 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.2.0.prompt.md +123 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_context_expert/v3.3.0.prompt.md +127 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.1.0.prompt.md +24 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main/v1.2.0.prompt.md +29 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.0.0.prompt.md +11 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.1.0.prompt.md +21 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_extraction_main_expert/v1.2.0.prompt.md +25 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.0.0.prompt.md +37 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.1.0.prompt.md +40 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_optimizer_judge/v1.2.0.prompt.md +36 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v1.0.0.prompt.md +45 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v2.0.0.prompt.md +81 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate/v3.0.0.prompt.md +80 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/playbook_should_generate_expert/v1.0.0.prompt.md +34 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_deduplication/v1.0.0.prompt.md +116 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_should_generate_override/v1.0.0.prompt.md +16 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.0.0.prompt.md +140 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_instruction_start/v1.1.0.prompt.md +160 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/profile_update_main/v1.0.0.prompt.md +14 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/query_reformulation/v1.0.0.prompt.md +19 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/rerank_relevance/v1.1.0.prompt.md +44 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_comparison/v1.0.0.prompt.md +43 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_bank/shadow_content_evaluation/v1.0.0.prompt.md +33 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/feedback_extraction_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_evaluation/prompt_evaluation_dataset/profile_update_main_v1.jsonl +10 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_manager.py +280 -0
- package/plugin/vendor/reflexio/reflexio/server/prompt/prompt_schema.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/_eval_health.py +131 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_constants.py +60 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_service.py +228 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluation_utils.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/agent_success_evaluator.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/delayed_group_evaluator.py +156 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/group_evaluation_runner.py +340 -0
- package/plugin/vendor/reflexio/reflexio/server/services/agent_success_evaluation/regen_jobs.py +471 -0
- package/plugin/vendor/reflexio/reflexio/server/services/base_generation_service.py +1626 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_cron.py +196 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/_encryption.py +101 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/client.py +167 -0
- package/plugin/vendor/reflexio/reflexio/server/services/braintrust/service.py +281 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/base_configurator.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/config_storage.py +62 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/configurator.py +87 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/local_file_config_storage.py +187 -0
- package/plugin/vendor/reflexio/reflexio/server/services/configurator/test_config_storage.py +162 -0
- package/plugin/vendor/reflexio/reflexio/server/services/deduplication_utils.py +112 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/distribution.py +33 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/eval_sampler.py +126 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/group_aggregation.py +192 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/hero_state.py +75 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/rule_attribution.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/service.py +515 -0
- package/plugin/vendor/reflexio/reflexio/server/services/evaluation_overview/shadow_aggregation.py +90 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/agent_run_records.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/invariants.py +303 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/outcome.py +25 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/pending_tool_call_dispatch.py +351 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/plan.py +138 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/prior_answer_search.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resumable_agent.py +468 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_scheduler.py +171 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/resume_worker.py +777 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extraction/tools.py +1125 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_config_utils.py +91 -0
- package/plugin/vendor/reflexio/reflexio/server/services/extractor_interaction_utils.py +251 -0
- package/plugin/vendor/reflexio/reflexio/server/services/generation_service.py +689 -0
- package/plugin/vendor/reflexio/reflexio/server/services/operation_state_utils.py +835 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/README.md +89 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_aggregator.py +1388 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_consolidator.py +960 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_extractor.py +436 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_generation_service.py +808 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_constants.py +28 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook/playbook_service_utils.py +362 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/__init__.py +24 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/assistant_webhook.py +246 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/gepa_adapter.py +291 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/judge.py +97 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/models.py +96 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/optimizer.py +645 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/rollout.py +35 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scenario_resolver.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/playbook_optimizer/scheduler.py +174 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/__init__.py +26 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_document_expander.py +179 -0
- package/plugin/vendor/reflexio/reflexio/server/services/pre_retrieval/_query_reformulator.py +297 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_deduplicator.py +741 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_extractor.py +462 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service.py +734 -0
- package/plugin/vendor/reflexio/reflexio/server/services/profile/profile_generation_service_utils.py +290 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/__init__.py +17 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_extractor.py +247 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service.py +800 -0
- package/plugin/vendor/reflexio/reflexio/server/services/reflection/reflection_service_utils.py +146 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/retrieval/relevance_floor.py +70 -0
- package/plugin/vendor/reflexio/reflexio/server/services/search/__init__.py +0 -0
- package/plugin/vendor/reflexio/reflexio/server/services/service_utils.py +671 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/judge.py +184 -0
- package/plugin/vendor/reflexio/reflexio/server/services/shadow_comparison/outcome.py +81 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/constants.py +2 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/error.py +11 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/retention_mixin.py +155 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/__init__.py +59 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_agent_run.py +1253 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_base.py +1945 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_extras.py +600 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_operations.py +346 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_playbook.py +1378 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_profiles.py +747 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_requests.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_shadow_verdicts.py +193 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_share_links.py +166 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/sqlite_storage/_stall_state.py +217 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/__init__.py +153 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_agent_run.py +372 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_base.py +71 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_extras.py +235 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_operations.py +170 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_playbook.py +677 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_profiles.py +250 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_requests.py +154 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_shadow_verdicts.py +130 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_share_links.py +93 -0
- package/plugin/vendor/reflexio/reflexio/server/services/storage/storage_base/_stall_state.py +76 -0
- package/plugin/vendor/reflexio/reflexio/server/services/unified_search_service.py +568 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/README.md +77 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/feature_flags.py +116 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_manager.py +263 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/feature_flags.json +13 -0
- package/plugin/vendor/reflexio/reflexio/server/site_var/site_var_sources/llm_model_setting.json +7 -0
- package/plugin/vendor/reflexio/reflexio/server/tracing.py +158 -0
- package/plugin/vendor/reflexio/reflexio/server/usage_metrics.py +113 -0
- package/plugin/vendor/reflexio/reflexio/server/uvicorn_logging.py +76 -0
- package/plugin/vendor/reflexio/reflexio/test_support/__init__.py +1 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_fixtures.py +62 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_mock.py +242 -0
- package/plugin/vendor/reflexio/reflexio/test_support/llm_model_registry.py +129 -0
- package/plugin/vendor/reflexio/reflexio/test_support/skip_decorators.py +43 -0
|
@@ -0,0 +1,671 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Utils for service layer
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
import ast
|
|
6
|
+
import json
|
|
7
|
+
import logging
|
|
8
|
+
import re
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from datetime import UTC, datetime
|
|
11
|
+
from typing import Any
|
|
12
|
+
|
|
13
|
+
from reflexio.cli.log_format import LLM_IO_LOG_FILE, next_llm_entry_id
|
|
14
|
+
from reflexio.models.api_schema.internal_schema import RequestInteractionDataModel
|
|
15
|
+
from reflexio.models.api_schema.service_schemas import (
|
|
16
|
+
Interaction,
|
|
17
|
+
UserActionType,
|
|
18
|
+
)
|
|
19
|
+
from reflexio.server import LLM_PROMPT_LEVEL
|
|
20
|
+
from reflexio.server.prompt.prompt_manager import PromptManager
|
|
21
|
+
|
|
22
|
+
logger = logging.getLogger(__name__)
|
|
23
|
+
|
|
24
|
+
# Custom log level for model responses (between INFO=20 and WARNING=30)
|
|
25
|
+
# Already registered in server/__init__.py; import the numeric constant only.
|
|
26
|
+
MODEL_RESPONSE_LEVEL = 25
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _format_response_for_logging(response: Any) -> Any:
|
|
30
|
+
"""Render ``ToolCallingChatResponse`` with pretty tool_calls; pass others through.
|
|
31
|
+
|
|
32
|
+
The dataclass's ``__repr__`` (which ``%s`` formatting falls back to)
|
|
33
|
+
prints each tool_call as an opaque object handle
|
|
34
|
+
(``<ChatCompletionMessageToolCall object at 0x…>``), erasing the
|
|
35
|
+
tool name + arguments the model emitted. This helper detects that
|
|
36
|
+
one case and renders a multi-line human-readable form using the
|
|
37
|
+
same ``_format_tool_calls`` helper the request-side formatter uses.
|
|
38
|
+
|
|
39
|
+
All other response types (strings, Pydantic ``BaseModel`` instances
|
|
40
|
+
from classic extractors / deduplicators / aggregators) fall through
|
|
41
|
+
unchanged so the existing log shape is preserved.
|
|
42
|
+
|
|
43
|
+
Lazy-imports ``ToolCallingChatResponse`` to avoid a circular
|
|
44
|
+
``service_utils`` ↔ ``litellm_client`` dependency at module load.
|
|
45
|
+
"""
|
|
46
|
+
try:
|
|
47
|
+
from reflexio.server.llm.litellm_client import ToolCallingChatResponse
|
|
48
|
+
except Exception: # noqa: BLE001 - fall back gracefully if the import fails
|
|
49
|
+
return response
|
|
50
|
+
|
|
51
|
+
if not isinstance(response, ToolCallingChatResponse):
|
|
52
|
+
return response
|
|
53
|
+
|
|
54
|
+
lines = [
|
|
55
|
+
f"ToolCallingChatResponse(finish_reason={response.finish_reason!r}):",
|
|
56
|
+
f" content: {response.content!r}",
|
|
57
|
+
]
|
|
58
|
+
if response.tool_calls:
|
|
59
|
+
lines.extend(_format_tool_calls(response.tool_calls))
|
|
60
|
+
else:
|
|
61
|
+
lines.append(" tool_calls: []")
|
|
62
|
+
return "\n".join(lines)
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def log_model_response(
|
|
66
|
+
target_logger: logging.Logger, label: str, response: Any
|
|
67
|
+
) -> None:
|
|
68
|
+
"""
|
|
69
|
+
Log an LLM model response. Full response goes to the log file at LLM_PROMPT level.
|
|
70
|
+
A one-line summary goes to the console at MODEL_RESPONSE level.
|
|
71
|
+
|
|
72
|
+
Args:
|
|
73
|
+
target_logger (logging.Logger): The logger instance to use
|
|
74
|
+
label (str): Descriptive label for the response (e.g. "Profile updates model response")
|
|
75
|
+
response (Any): The model response to log
|
|
76
|
+
"""
|
|
77
|
+
entry_id = next_llm_entry_id()
|
|
78
|
+
# Special-case ToolCallingChatResponse so tool_calls render as
|
|
79
|
+
# id/name/arguments instead of opaque ``<… object at 0x…>`` handles.
|
|
80
|
+
formatted = _format_response_for_logging(response)
|
|
81
|
+
# Full response to llm_io.log only (level 15 < INFO 20, so console ignores it)
|
|
82
|
+
target_logger.log(
|
|
83
|
+
LLM_PROMPT_LEVEL,
|
|
84
|
+
"[#%d] %s: %s",
|
|
85
|
+
entry_id,
|
|
86
|
+
label,
|
|
87
|
+
formatted,
|
|
88
|
+
extra={"entry_id": entry_id, "label": label},
|
|
89
|
+
)
|
|
90
|
+
# One-line summary to console
|
|
91
|
+
response_type = type(response).__name__
|
|
92
|
+
target_logger.log(
|
|
93
|
+
MODEL_RESPONSE_LEVEL,
|
|
94
|
+
"[MODEL] %s: %s — %s [#%d]",
|
|
95
|
+
label,
|
|
96
|
+
response_type,
|
|
97
|
+
LLM_IO_LOG_FILE,
|
|
98
|
+
entry_id,
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def log_llm_messages(
|
|
103
|
+
target_logger: logging.Logger,
|
|
104
|
+
label: str,
|
|
105
|
+
messages: list[dict[str, Any]],
|
|
106
|
+
) -> None:
|
|
107
|
+
"""
|
|
108
|
+
Log LLM prompt messages. Full content goes to the log file at LLM_PROMPT level.
|
|
109
|
+
A one-line summary goes to the console at INFO level.
|
|
110
|
+
|
|
111
|
+
Args:
|
|
112
|
+
target_logger (logging.Logger): The logger instance to use
|
|
113
|
+
label (str): Descriptive label (e.g. "Profile extraction")
|
|
114
|
+
messages (list[dict[str, Any]]): The LLM messages to log
|
|
115
|
+
"""
|
|
116
|
+
entry_id = next_llm_entry_id()
|
|
117
|
+
# Full messages to llm_io.log only
|
|
118
|
+
formatted = format_messages_for_logging(messages)
|
|
119
|
+
target_logger.log(
|
|
120
|
+
LLM_PROMPT_LEVEL,
|
|
121
|
+
"%s messages:\n%s",
|
|
122
|
+
label,
|
|
123
|
+
formatted,
|
|
124
|
+
extra={"entry_id": entry_id, "label": label},
|
|
125
|
+
)
|
|
126
|
+
# Summary to console
|
|
127
|
+
total_chars = sum(len(str(msg.get("content", ""))) for msg in messages)
|
|
128
|
+
target_logger.info(
|
|
129
|
+
"[LLM] %s: %d msgs, ~%d chars — %s [#%d]",
|
|
130
|
+
label,
|
|
131
|
+
len(messages),
|
|
132
|
+
total_chars,
|
|
133
|
+
LLM_IO_LOG_FILE,
|
|
134
|
+
entry_id,
|
|
135
|
+
)
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@dataclass
|
|
139
|
+
class PromptConfig:
|
|
140
|
+
"""Configuration for a prompt to be rendered.
|
|
141
|
+
|
|
142
|
+
Attributes:
|
|
143
|
+
prompt_id: The ID of the prompt template to render
|
|
144
|
+
variables: Dictionary of variables to pass to the prompt template
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
prompt_id: str
|
|
148
|
+
variables: dict[str, any] # type: ignore[reportGeneralTypeIssues]
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
@dataclass
|
|
152
|
+
class MessageConstructionConfig:
|
|
153
|
+
"""Configuration for constructing LLM messages from interactions.
|
|
154
|
+
|
|
155
|
+
Attributes:
|
|
156
|
+
prompt_manager: The prompt manager to use for rendering prompts
|
|
157
|
+
system_prompt_config: Configuration for the system message (before interactions)
|
|
158
|
+
user_prompt_config: Configuration for the user message (after interactions)
|
|
159
|
+
"""
|
|
160
|
+
|
|
161
|
+
prompt_manager: PromptManager
|
|
162
|
+
system_prompt_config: PromptConfig | None = None
|
|
163
|
+
user_prompt_config: PromptConfig | None = None
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def format_interactions_to_history_string(interactions: list[Interaction]) -> str:
|
|
167
|
+
"""
|
|
168
|
+
Format a list of interactions into a single string representing the interaction history.
|
|
169
|
+
|
|
170
|
+
Each interaction is formatted as:
|
|
171
|
+
- Text content: "{role}: {content}"
|
|
172
|
+
- User actions: "{role}: {action} {action_description}"
|
|
173
|
+
|
|
174
|
+
Args:
|
|
175
|
+
interactions (list[Interaction]): List of interactions to format
|
|
176
|
+
|
|
177
|
+
Returns:
|
|
178
|
+
str: A formatted string representing the interaction history, with interactions separated by newlines.
|
|
179
|
+
Returns empty string if no interactions are provided.
|
|
180
|
+
|
|
181
|
+
Example:
|
|
182
|
+
>>> interactions = [
|
|
183
|
+
... Interaction(role="user", content="I love sushi", user_action=UserActionType.NONE),
|
|
184
|
+
... Interaction(role="user", content="", user_action=UserActionType.CLICK, user_action_description="menu item")
|
|
185
|
+
... ]
|
|
186
|
+
>>> result = format_interactions_to_history_string(interactions)
|
|
187
|
+
>>> print(result)
|
|
188
|
+
user: I love sushi
|
|
189
|
+
user: click menu item
|
|
190
|
+
"""
|
|
191
|
+
formatted_interactions = []
|
|
192
|
+
for interaction in interactions:
|
|
193
|
+
# Add text content with tools_used prefix if present
|
|
194
|
+
if interaction.content:
|
|
195
|
+
if interaction.tools_used:
|
|
196
|
+
tool_prefix = " ".join(
|
|
197
|
+
f"[used tool: {t.tool_name}({json.dumps(t.tool_data)})]"
|
|
198
|
+
for t in interaction.tools_used
|
|
199
|
+
)
|
|
200
|
+
formatted_interactions.append(
|
|
201
|
+
f"{interaction.role}: ```{tool_prefix} {interaction.content}```"
|
|
202
|
+
)
|
|
203
|
+
else:
|
|
204
|
+
formatted_interactions.append(
|
|
205
|
+
f"{interaction.role}: ```{interaction.content}```"
|
|
206
|
+
)
|
|
207
|
+
|
|
208
|
+
# Add user action
|
|
209
|
+
if interaction.user_action != UserActionType.NONE:
|
|
210
|
+
formatted_interactions.append(
|
|
211
|
+
f"{interaction.role}: ```{interaction.user_action.value} {interaction.user_action_description}```"
|
|
212
|
+
)
|
|
213
|
+
|
|
214
|
+
return "\n".join(formatted_interactions)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
def format_sessions_to_history_string(
|
|
218
|
+
sessions: list[RequestInteractionDataModel],
|
|
219
|
+
) -> str:
|
|
220
|
+
"""
|
|
221
|
+
Format interactions grouped by session into a string.
|
|
222
|
+
|
|
223
|
+
All RequestInteractionDataModel objects with the same session_id are consolidated
|
|
224
|
+
under a single header. Within each consolidated group, interactions are ordered by
|
|
225
|
+
their id in ascending order (smaller to bigger).
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
sessions (list[RequestInteractionDataModel]): List of request interaction data models to format
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
str: A formatted string with interactions grouped by session.
|
|
232
|
+
Returns empty string if no sessions are provided.
|
|
233
|
+
|
|
234
|
+
Example:
|
|
235
|
+
>>> # Given sessions with interactions (multiple requests in same session)
|
|
236
|
+
>>> result = format_sessions_to_history_string(sessions)
|
|
237
|
+
>>> print(result)
|
|
238
|
+
=== Session: session_1 ===
|
|
239
|
+
user: Hello, I need help
|
|
240
|
+
assistant: How can I assist you?
|
|
241
|
+
user: Thanks for the help
|
|
242
|
+
assistant: You're welcome!
|
|
243
|
+
|
|
244
|
+
=== Session: session_2 ===
|
|
245
|
+
user: I love sushi
|
|
246
|
+
assistant: That's great!
|
|
247
|
+
"""
|
|
248
|
+
if not sessions:
|
|
249
|
+
return ""
|
|
250
|
+
|
|
251
|
+
# Group all RequestInteractionDataModel objects by their session_id
|
|
252
|
+
grouped_by_name: dict[str, list[RequestInteractionDataModel]] = {}
|
|
253
|
+
for request_interaction in sessions:
|
|
254
|
+
if request_interaction.session_id not in grouped_by_name:
|
|
255
|
+
grouped_by_name[request_interaction.session_id] = []
|
|
256
|
+
grouped_by_name[request_interaction.session_id].append(request_interaction)
|
|
257
|
+
|
|
258
|
+
# Sort each group's requests by created_at timestamp
|
|
259
|
+
for group_name in grouped_by_name:
|
|
260
|
+
grouped_by_name[group_name] = sorted(
|
|
261
|
+
grouped_by_name[group_name], key=lambda g: g.request.created_at
|
|
262
|
+
)
|
|
263
|
+
|
|
264
|
+
# Sort group names by the earliest request timestamp in each group
|
|
265
|
+
sorted_group_names = sorted(
|
|
266
|
+
grouped_by_name.keys(),
|
|
267
|
+
key=lambda name: grouped_by_name[name][0].request.created_at,
|
|
268
|
+
)
|
|
269
|
+
|
|
270
|
+
formatted_groups = []
|
|
271
|
+
for group_name in sorted_group_names:
|
|
272
|
+
# Format header with session name AND its earliest interaction date.
|
|
273
|
+
# Without the date, downstream extraction agents have no anchor for
|
|
274
|
+
# resolving relative-time references in the conversation
|
|
275
|
+
# ("X weeks ago", "yesterday", "two days before the wedding") —
|
|
276
|
+
# they fall back to real-world `now()` and encode every event as
|
|
277
|
+
# today's date, breaking temporal-reasoning queries.
|
|
278
|
+
#
|
|
279
|
+
# We use the earliest *interaction* timestamp, not request.created_at,
|
|
280
|
+
# because Request.created_at defaults to `now()` on construction —
|
|
281
|
+
# only interactions reliably carry the conversation's true wall-clock
|
|
282
|
+
# time when the publisher provides it.
|
|
283
|
+
all_ts: list[int] = [
|
|
284
|
+
i.created_at
|
|
285
|
+
for ri in grouped_by_name[group_name]
|
|
286
|
+
for i in ri.interactions
|
|
287
|
+
if i.created_at
|
|
288
|
+
]
|
|
289
|
+
first_ts = min(all_ts) if all_ts else 0
|
|
290
|
+
if first_ts:
|
|
291
|
+
try:
|
|
292
|
+
session_date_iso = datetime.fromtimestamp(first_ts, tz=UTC).strftime(
|
|
293
|
+
"%Y-%m-%d"
|
|
294
|
+
)
|
|
295
|
+
group_header = (
|
|
296
|
+
f"=== Session: {group_name} (date: {session_date_iso}) ==="
|
|
297
|
+
)
|
|
298
|
+
except (OverflowError, OSError, ValueError):
|
|
299
|
+
group_header = f"=== Session: {group_name} ==="
|
|
300
|
+
else:
|
|
301
|
+
group_header = f"=== Session: {group_name} ==="
|
|
302
|
+
|
|
303
|
+
# Combine all interactions from all requests in this session
|
|
304
|
+
all_interactions = []
|
|
305
|
+
for request_interaction in grouped_by_name[group_name]:
|
|
306
|
+
all_interactions.extend(request_interaction.interactions)
|
|
307
|
+
|
|
308
|
+
# Sort interactions by id ascending (smaller to bigger)
|
|
309
|
+
all_interactions = sorted(all_interactions, key=lambda i: i.interaction_id)
|
|
310
|
+
|
|
311
|
+
# Format combined interactions
|
|
312
|
+
group_interactions = format_interactions_to_history_string(all_interactions)
|
|
313
|
+
|
|
314
|
+
# Combine header and interactions
|
|
315
|
+
formatted_groups.append(f"{group_header}\n{group_interactions}")
|
|
316
|
+
|
|
317
|
+
return "\n\n".join(formatted_groups)
|
|
318
|
+
|
|
319
|
+
|
|
320
|
+
def extract_interactions_from_request_interaction_data_models(
|
|
321
|
+
request_interaction_data_models: list[RequestInteractionDataModel],
|
|
322
|
+
) -> list[Interaction]:
|
|
323
|
+
"""
|
|
324
|
+
Extract a flat list of interactions from request interaction groups.
|
|
325
|
+
|
|
326
|
+
This is useful for backward compatibility with services that still expect
|
|
327
|
+
a flat list of interactions.
|
|
328
|
+
|
|
329
|
+
Args:
|
|
330
|
+
request_interaction_data_models (list[RequestInteractionDataModel]): List of request interaction data models
|
|
331
|
+
|
|
332
|
+
Returns:
|
|
333
|
+
list[Interaction]: Flat list of all interactions from all groups
|
|
334
|
+
"""
|
|
335
|
+
interactions = []
|
|
336
|
+
for request_interaction_data_model in request_interaction_data_models:
|
|
337
|
+
interactions.extend(request_interaction_data_model.interactions)
|
|
338
|
+
return interactions
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
def construct_messages_from_interactions(
|
|
342
|
+
interactions: list[Interaction],
|
|
343
|
+
config: MessageConstructionConfig,
|
|
344
|
+
) -> list[dict]:
|
|
345
|
+
"""
|
|
346
|
+
Construct a list of LLM messages from interactions with custom prompts.
|
|
347
|
+
|
|
348
|
+
This function creates a structured message sequence:
|
|
349
|
+
1. Optional system message (using system_prompt_config)
|
|
350
|
+
2. Single user message containing:
|
|
351
|
+
- Formatted interactions (text content and user actions)
|
|
352
|
+
- User prompt (if configured)
|
|
353
|
+
- Last interaction's image (if present)
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
interactions: List of interactions to convert into messages
|
|
357
|
+
config: Configuration for message construction including prompt configs
|
|
358
|
+
|
|
359
|
+
Returns:
|
|
360
|
+
list[dict]: List of messages ready to be sent to the LLM API.
|
|
361
|
+
Each message is a dict with 'role' and 'content' keys.
|
|
362
|
+
|
|
363
|
+
Example:
|
|
364
|
+
>>> system_config = PromptConfig(
|
|
365
|
+
... prompt_id="profile_update_instruction_start",
|
|
366
|
+
... variables={"agent_context_prompt": "...", "context_prompt": "..."}
|
|
367
|
+
... )
|
|
368
|
+
>>> user_config = PromptConfig(
|
|
369
|
+
... prompt_id="profile_update_main",
|
|
370
|
+
... variables={"extraction_definition_prompt": "...", "existing_profiles": "..."}
|
|
371
|
+
... )
|
|
372
|
+
>>> config = MessageConstructionConfig(
|
|
373
|
+
... prompt_manager=prompt_manager,
|
|
374
|
+
... system_prompt_config=system_config,
|
|
375
|
+
... user_prompt_config=user_config
|
|
376
|
+
... )
|
|
377
|
+
>>> messages = construct_messages_from_interactions(interactions, config)
|
|
378
|
+
"""
|
|
379
|
+
messages = []
|
|
380
|
+
|
|
381
|
+
# Add system message if configured
|
|
382
|
+
if config.system_prompt_config:
|
|
383
|
+
system_content = config.prompt_manager.render_prompt(
|
|
384
|
+
config.system_prompt_config.prompt_id,
|
|
385
|
+
config.system_prompt_config.variables,
|
|
386
|
+
)
|
|
387
|
+
messages.append({"role": "system", "content": system_content})
|
|
388
|
+
|
|
389
|
+
# Build combined user message content
|
|
390
|
+
combined_content = []
|
|
391
|
+
|
|
392
|
+
# Add user prompt if configured
|
|
393
|
+
if config.user_prompt_config:
|
|
394
|
+
user_prompt_content = config.prompt_manager.render_prompt(
|
|
395
|
+
config.user_prompt_config.prompt_id,
|
|
396
|
+
config.user_prompt_config.variables,
|
|
397
|
+
)
|
|
398
|
+
combined_content.append({"type": "text", "text": user_prompt_content})
|
|
399
|
+
|
|
400
|
+
# Add last interaction's image (if present)
|
|
401
|
+
if interactions:
|
|
402
|
+
last_interaction = interactions[-1]
|
|
403
|
+
|
|
404
|
+
# Add image URL (priority over base64)
|
|
405
|
+
if last_interaction.interacted_image_url:
|
|
406
|
+
combined_content.append(
|
|
407
|
+
{
|
|
408
|
+
"type": "image_url",
|
|
409
|
+
"image_url": {"url": last_interaction.interacted_image_url},
|
|
410
|
+
}
|
|
411
|
+
)
|
|
412
|
+
# Add base64-encoded image if no URL
|
|
413
|
+
elif last_interaction.image_encoding:
|
|
414
|
+
combined_content.append(
|
|
415
|
+
{
|
|
416
|
+
"type": "image_url",
|
|
417
|
+
"image_url": {
|
|
418
|
+
"url": f"data:image/jpeg;base64,{last_interaction.image_encoding}"
|
|
419
|
+
},
|
|
420
|
+
}
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
# Add combined user message if there's any content
|
|
424
|
+
if combined_content:
|
|
425
|
+
# Flatten to plain string when all blocks are text-only (no images).
|
|
426
|
+
# Content-block format combined with structured output (response_format)
|
|
427
|
+
# causes significantly slower OpenAI API responses and timeouts.
|
|
428
|
+
all_text = all(block.get("type") == "text" for block in combined_content)
|
|
429
|
+
if all_text:
|
|
430
|
+
content = "\n\n".join(block["text"] for block in combined_content)
|
|
431
|
+
else:
|
|
432
|
+
content = combined_content
|
|
433
|
+
messages.append({"role": "user", "content": content})
|
|
434
|
+
|
|
435
|
+
return messages
|
|
436
|
+
|
|
437
|
+
|
|
438
|
+
def extract_json_from_string(text: str) -> dict:
|
|
439
|
+
"""
|
|
440
|
+
Extract JSON from a string, handling both JSON-style and Python-style booleans.
|
|
441
|
+
|
|
442
|
+
This function attempts to extract JSON from text in the following order:
|
|
443
|
+
1. From code blocks (```json...```)
|
|
444
|
+
2. From content between first { and last }
|
|
445
|
+
|
|
446
|
+
It also handles Python-style boolean values (True/False/None) by converting
|
|
447
|
+
them to JSON-style (true/false/null) before parsing, and falls back to
|
|
448
|
+
Python literal parsing for dict-like responses that use single quotes.
|
|
449
|
+
|
|
450
|
+
Args:
|
|
451
|
+
text (str): string to extract JSON from
|
|
452
|
+
|
|
453
|
+
Returns:
|
|
454
|
+
dict: JSON object, or empty dict if parsing fails
|
|
455
|
+
"""
|
|
456
|
+
|
|
457
|
+
def normalize_json_string(json_str: str) -> str:
|
|
458
|
+
"""Convert Python-style syntax to JSON-friendly syntax.
|
|
459
|
+
|
|
460
|
+
Handles:
|
|
461
|
+
- Python booleans (True/False/None) -> JSON (true/false/null)
|
|
462
|
+
"""
|
|
463
|
+
# Replace Python boolean/null values with JSON equivalents
|
|
464
|
+
# Use word boundaries to avoid replacing parts of strings
|
|
465
|
+
json_str = re.sub(r"\bTrue\b", "true", json_str)
|
|
466
|
+
json_str = re.sub(r"\bFalse\b", "false", json_str)
|
|
467
|
+
json_str = re.sub(r"\bNone\b", "null", json_str)
|
|
468
|
+
|
|
469
|
+
return json_str # noqa: RET504
|
|
470
|
+
|
|
471
|
+
def fix_unescaped_inner_quotes(json_str: str) -> str:
|
|
472
|
+
"""
|
|
473
|
+
Attempt to fix common cases where apostrophes are returned as double quotes.
|
|
474
|
+
|
|
475
|
+
Many LLMs occasionally substitute `customer's` with `customer"s`, leaving the JSON
|
|
476
|
+
invalid because the double quote isn't escaped. We treat double quotes that sit
|
|
477
|
+
between two word characters as apostrophes.
|
|
478
|
+
"""
|
|
479
|
+
return re.sub(r"(?<=\w)\"(?=\w)", "'", json_str)
|
|
480
|
+
|
|
481
|
+
def parse_json_candidate(json_str: str) -> tuple[dict | None, str | None]:
|
|
482
|
+
"""
|
|
483
|
+
Try to parse a JSON candidate string using multiple strategies.
|
|
484
|
+
|
|
485
|
+
Strategies:
|
|
486
|
+
1. Direct json.loads
|
|
487
|
+
2. json.loads after normalizing Python syntax
|
|
488
|
+
3. ast.literal_eval as a fallback for Python-style dicts
|
|
489
|
+
"""
|
|
490
|
+
candidates = [json_str]
|
|
491
|
+
last_error: str | None = None
|
|
492
|
+
|
|
493
|
+
normalized_json_str = normalize_json_string(json_str)
|
|
494
|
+
if normalized_json_str != json_str:
|
|
495
|
+
candidates.append(normalized_json_str)
|
|
496
|
+
|
|
497
|
+
repaired_json_str = fix_unescaped_inner_quotes(normalized_json_str)
|
|
498
|
+
if repaired_json_str not in candidates:
|
|
499
|
+
candidates.append(repaired_json_str)
|
|
500
|
+
|
|
501
|
+
for candidate in candidates:
|
|
502
|
+
try:
|
|
503
|
+
parsed = json.loads(candidate)
|
|
504
|
+
if isinstance(parsed, dict):
|
|
505
|
+
return parsed, None
|
|
506
|
+
except json.JSONDecodeError as err: # noqa: PERF203
|
|
507
|
+
last_error = str(err)
|
|
508
|
+
continue
|
|
509
|
+
|
|
510
|
+
for candidate in candidates:
|
|
511
|
+
try:
|
|
512
|
+
parsed = ast.literal_eval(candidate)
|
|
513
|
+
if isinstance(parsed, dict):
|
|
514
|
+
return parsed, None
|
|
515
|
+
except (ValueError, SyntaxError) as err: # noqa: PERF203
|
|
516
|
+
last_error = str(err)
|
|
517
|
+
continue
|
|
518
|
+
|
|
519
|
+
return None, last_error
|
|
520
|
+
|
|
521
|
+
# Pattern to match content between triple backticks and json keyword
|
|
522
|
+
# Allow optional whitespace around json keyword and content
|
|
523
|
+
pattern = r"```json\s*(.*?)\s*```"
|
|
524
|
+
|
|
525
|
+
# Find the match using regex
|
|
526
|
+
match = re.search(pattern, text, re.DOTALL)
|
|
527
|
+
|
|
528
|
+
if match:
|
|
529
|
+
json_str = match.group(1)
|
|
530
|
+
parsed_json, error_message = parse_json_candidate(json_str)
|
|
531
|
+
if parsed_json is not None:
|
|
532
|
+
return parsed_json
|
|
533
|
+
if error_message:
|
|
534
|
+
logger.warning("Failed to parse JSON from code block: %s", error_message)
|
|
535
|
+
|
|
536
|
+
# Try to find JSON content between first { and last }
|
|
537
|
+
start_idx = text.find("{")
|
|
538
|
+
end_idx = text.rfind("}")
|
|
539
|
+
if start_idx != -1 and end_idx != -1:
|
|
540
|
+
json_str = text[start_idx : end_idx + 1]
|
|
541
|
+
parsed_json, error_message = parse_json_candidate(json_str)
|
|
542
|
+
if parsed_json is not None:
|
|
543
|
+
return parsed_json
|
|
544
|
+
if error_message:
|
|
545
|
+
logger.warning("Failed to parse JSON from braces: %s", error_message)
|
|
546
|
+
|
|
547
|
+
return {}
|
|
548
|
+
|
|
549
|
+
|
|
550
|
+
def _format_tool_calls(tool_calls: list[Any]) -> list[str]:
|
|
551
|
+
"""Render an assistant message's ``tool_calls`` list for the log.
|
|
552
|
+
|
|
553
|
+
Accepts either the OpenAI SDK object shape (with ``.function.name`` /
|
|
554
|
+
``.function.arguments`` attrs) or the dict shape that pass-through
|
|
555
|
+
serialisation may produce. Returns one indented line per call with the
|
|
556
|
+
tool_call_id, the tool name, and the parsed arguments — so the log
|
|
557
|
+
reader can correlate each tool_call with its tool-role response.
|
|
558
|
+
"""
|
|
559
|
+
lines: list[str] = [" tool_calls:"]
|
|
560
|
+
for tc in tool_calls:
|
|
561
|
+
# Extract id, name, arguments from either attribute or mapping shape.
|
|
562
|
+
tc_id = getattr(tc, "id", None) or (
|
|
563
|
+
tc.get("id") if isinstance(tc, dict) else None
|
|
564
|
+
)
|
|
565
|
+
fn = getattr(tc, "function", None)
|
|
566
|
+
if fn is not None:
|
|
567
|
+
name = getattr(fn, "name", None)
|
|
568
|
+
args_raw = getattr(fn, "arguments", None)
|
|
569
|
+
elif isinstance(tc, dict):
|
|
570
|
+
fn_dict = tc.get("function", {}) or {}
|
|
571
|
+
name = fn_dict.get("name") if isinstance(fn_dict, dict) else None
|
|
572
|
+
args_raw = fn_dict.get("arguments") if isinstance(fn_dict, dict) else None
|
|
573
|
+
else:
|
|
574
|
+
name = None
|
|
575
|
+
args_raw = None
|
|
576
|
+
|
|
577
|
+
# arguments comes through as a JSON string from the provider — parse
|
|
578
|
+
# for readability, fall back to raw text on malformed JSON.
|
|
579
|
+
parsed_args: Any
|
|
580
|
+
if isinstance(args_raw, str):
|
|
581
|
+
try:
|
|
582
|
+
parsed_args = json.loads(args_raw)
|
|
583
|
+
except json.JSONDecodeError:
|
|
584
|
+
parsed_args = args_raw
|
|
585
|
+
else:
|
|
586
|
+
parsed_args = args_raw
|
|
587
|
+
|
|
588
|
+
lines.append(f" - id: {tc_id}")
|
|
589
|
+
lines.append(f" name: {name}")
|
|
590
|
+
# Logging path must never raise — fall back to repr() on
|
|
591
|
+
# non-serializable argument objects (datetime, sets, custom
|
|
592
|
+
# types, etc.) so a logging call can't take down a request.
|
|
593
|
+
try:
|
|
594
|
+
rendered_args = json.dumps(parsed_args)
|
|
595
|
+
except (TypeError, ValueError):
|
|
596
|
+
rendered_args = repr(parsed_args)
|
|
597
|
+
lines.append(f" arguments: {rendered_args}")
|
|
598
|
+
return lines
|
|
599
|
+
|
|
600
|
+
|
|
601
|
+
def format_messages_for_logging(messages: list[dict[str, Any]]) -> str:
|
|
602
|
+
"""
|
|
603
|
+
Format messages for logging with proper newlines in text content.
|
|
604
|
+
|
|
605
|
+
Args:
|
|
606
|
+
messages: List of message dictionaries with role and content
|
|
607
|
+
|
|
608
|
+
Returns:
|
|
609
|
+
str: Formatted string representation of messages with newlines preserved
|
|
610
|
+
"""
|
|
611
|
+
formatted_parts = []
|
|
612
|
+
for i, msg in enumerate(messages):
|
|
613
|
+
formatted_parts.append(f"Message {i + 1}:")
|
|
614
|
+
formatted_parts.append(f" role: {msg.get('role', 'unknown')}")
|
|
615
|
+
|
|
616
|
+
# Tool-role messages carry a ``tool_call_id`` that correlates them
|
|
617
|
+
# back to the assistant's emitted call — render it so readers can
|
|
618
|
+
# reconstruct which response answered which call.
|
|
619
|
+
tool_call_id = msg.get("tool_call_id")
|
|
620
|
+
if tool_call_id is not None:
|
|
621
|
+
formatted_parts.append(f" tool_call_id: {tool_call_id}")
|
|
622
|
+
|
|
623
|
+
content = msg.get("content", "")
|
|
624
|
+
|
|
625
|
+
if isinstance(content, str):
|
|
626
|
+
# Simple string content - preserve newlines
|
|
627
|
+
formatted_parts.append(" content:")
|
|
628
|
+
# Indent each line of content
|
|
629
|
+
formatted_parts.extend(f" {line}" for line in content.split("\n"))
|
|
630
|
+
elif isinstance(content, list):
|
|
631
|
+
# Multimodal content (list of objects)
|
|
632
|
+
formatted_parts.append(" content:")
|
|
633
|
+
for item in content:
|
|
634
|
+
if isinstance(item, dict):
|
|
635
|
+
item_type = item.get("type", "unknown")
|
|
636
|
+
if item_type == "text":
|
|
637
|
+
text_content = item.get("text", "")
|
|
638
|
+
formatted_parts.append(f" type: {item_type}")
|
|
639
|
+
formatted_parts.append(" text:")
|
|
640
|
+
# Indent each line of text
|
|
641
|
+
formatted_parts.extend(
|
|
642
|
+
f" {line}" for line in text_content.split("\n")
|
|
643
|
+
)
|
|
644
|
+
else:
|
|
645
|
+
# For non-text content, use JSON representation
|
|
646
|
+
formatted_parts.append(f" {json.dumps(item, indent=4)}")
|
|
647
|
+
else:
|
|
648
|
+
formatted_parts.append(f" {json.dumps(item, indent=4)}")
|
|
649
|
+
else:
|
|
650
|
+
# Fallback to JSON for other types
|
|
651
|
+
formatted_parts.append(f" content: {json.dumps(content, indent=4)}")
|
|
652
|
+
|
|
653
|
+
# Assistant messages with tool_calls must render the call list —
|
|
654
|
+
# otherwise the log shows ``content: null`` with no visibility into
|
|
655
|
+
# which tools the model invoked. Classic extraction doesn't use
|
|
656
|
+
# tool-calling, but the agentic pipeline relies on it heavily.
|
|
657
|
+
tool_calls = msg.get("tool_calls")
|
|
658
|
+
if tool_calls:
|
|
659
|
+
formatted_parts.extend(_format_tool_calls(tool_calls))
|
|
660
|
+
|
|
661
|
+
formatted_parts.append("") # Empty line between messages
|
|
662
|
+
|
|
663
|
+
return "\n".join(formatted_parts)
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
# Example usage
|
|
667
|
+
if __name__ == "__main__":
|
|
668
|
+
test_string = """'Based on the existing profiles (which are currently empty) and the new interaction indicating a preference for sushi, the update would involve adding a new profile that reflects this preference. Here's the JSON format for the updates:\n\n```json\n{\n "add_profile": ["I like sushi"],\n "delete_profile": []\n}\n```'"""
|
|
669
|
+
|
|
670
|
+
result = extract_json_from_string(test_string)
|
|
671
|
+
print("Extracted JSON:", result)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""F1 per-turn shadow comparison service."""
|