marvisx-cli 0.1.0__py3-none-any.whl
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.
- core/api/__init__.py +0 -0
- core/api/agents/__init__.py +0 -0
- core/api/agents/session_health.py +59 -0
- core/api/agents/session_manager.py +206 -0
- core/api/bin/marvisx-state-hook.py +182 -0
- core/api/config.py +533 -0
- core/api/db.py +1516 -0
- core/api/dependencies/__init__.py +0 -0
- core/api/dependencies/tenant.py +34 -0
- core/api/main.py +1641 -0
- core/api/mcp/__init__.py +8 -0
- core/api/mcp/_adapter.py +184 -0
- core/api/mcp/server.py +58 -0
- core/api/mcp/tools/__init__.py +59 -0
- core/api/mcp/tools/brain.py +599 -0
- core/api/mcp/tools/graph.py +380 -0
- core/api/mcp/tools/handoffs.py +112 -0
- core/api/mcp/tools/ingest.py +326 -0
- core/api/mcp/tools/learnings.py +144 -0
- core/api/mcp/tools/projects.py +99 -0
- core/api/mcp/tools/pull_requests.py +173 -0
- core/api/mcp/tools/safety.py +111 -0
- core/api/mcp/tools/search.py +79 -0
- core/api/mcp/tools/tasks.py +258 -0
- core/api/middleware/__init__.py +0 -0
- core/api/middleware/tool_call_audit.py +111 -0
- core/api/models/__init__.py +346 -0
- core/api/models/auth.py +48 -0
- core/api/models/brain.py +1006 -0
- core/api/models/common.py +76 -0
- core/api/models/costs.py +91 -0
- core/api/models/graph.py +66 -0
- core/api/models/graph_cosmo.py +125 -0
- core/api/models/graph_pr_impact.py +257 -0
- core/api/models/graph_ux.py +141 -0
- core/api/models/inbox.py +230 -0
- core/api/models/ingest_keys.py +108 -0
- core/api/models/kg.py +41 -0
- core/api/models/llm_config.py +56 -0
- core/api/models/monitoring.py +234 -0
- core/api/models/projects.py +161 -0
- core/api/models/search.py +42 -0
- core/api/models/sessions.py +322 -0
- core/api/models/tasks.py +184 -0
- core/api/models/teams.py +63 -0
- core/api/models/users.py +184 -0
- core/api/observability/__init__.py +0 -0
- core/api/observability/tracing.py +92 -0
- core/api/paths.py +26 -0
- core/api/rate_limit.py +24 -0
- core/api/rbac.py +112 -0
- core/api/routers/__init__.py +0 -0
- core/api/routers/_adapter.py +24 -0
- core/api/routers/admin_pr_impact.py +230 -0
- core/api/routers/admin_settings.py +147 -0
- core/api/routers/agent.py +1079 -0
- core/api/routers/agent_tokens.py +276 -0
- core/api/routers/app_settings.py +112 -0
- core/api/routers/audit.py +89 -0
- core/api/routers/auth.py +586 -0
- core/api/routers/bench.py +161 -0
- core/api/routers/brain.py +881 -0
- core/api/routers/brain_directions.py +527 -0
- core/api/routers/ci_checks.py +140 -0
- core/api/routers/comments.py +273 -0
- core/api/routers/costs.py +148 -0
- core/api/routers/docs_coverage.py +217 -0
- core/api/routers/docs_governance.py +63 -0
- core/api/routers/documents.py +318 -0
- core/api/routers/files.py +163 -0
- core/api/routers/finder.py +987 -0
- core/api/routers/graph.py +836 -0
- core/api/routers/handoffs.py +156 -0
- core/api/routers/inbox.py +496 -0
- core/api/routers/ingest_api_keys.py +205 -0
- core/api/routers/ingest_triage.py +1227 -0
- core/api/routers/judge.py +306 -0
- core/api/routers/kg.py +336 -0
- core/api/routers/learnings.py +253 -0
- core/api/routers/llm_config.py +130 -0
- core/api/routers/monitoring.py +347 -0
- core/api/routers/notifications.py +125 -0
- core/api/routers/pr_impact.py +315 -0
- core/api/routers/projects.py +1061 -0
- core/api/routers/pull_requests.py +312 -0
- core/api/routers/push.py +67 -0
- core/api/routers/raci.py +228 -0
- core/api/routers/search.py +125 -0
- core/api/routers/sessions.py +3100 -0
- core/api/routers/settings.py +90 -0
- core/api/routers/share_repo.py +68 -0
- core/api/routers/status_updates.py +96 -0
- core/api/routers/tags.py +45 -0
- core/api/routers/tasks.py +526 -0
- core/api/routers/teams.py +425 -0
- core/api/routers/terminal.py +105 -0
- core/api/routers/users.py +331 -0
- core/api/routers/webhooks.py +330 -0
- core/api/runtime_settings.py +84 -0
- core/api/security.py +652 -0
- core/api/services/__init__.py +0 -0
- core/api/services/audit.py +58 -0
- core/api/services/auto_approval.py +82 -0
- core/api/services/brain/__init__.py +52 -0
- core/api/services/brain/baseline.py +230 -0
- core/api/services/brain/capabilities.py +75 -0
- core/api/services/brain/cascade_rollup.py +388 -0
- core/api/services/brain/compound_bridge.py +215 -0
- core/api/services/brain/cycle.py +1242 -0
- core/api/services/brain/cycle_snapshot.py +371 -0
- core/api/services/brain/digest_collector.py +147 -0
- core/api/services/brain/direction.py +421 -0
- core/api/services/brain/drift.py +356 -0
- core/api/services/brain/drift_router.py +409 -0
- core/api/services/brain/edge_metrics.py +79 -0
- core/api/services/brain/events_reader.py +222 -0
- core/api/services/brain/findings.py +1379 -0
- core/api/services/brain/findings_reader.py +1006 -0
- core/api/services/brain/jobs.py +733 -0
- core/api/services/brain/journal.py +206 -0
- core/api/services/brain/knowledge_forms.py +92 -0
- core/api/services/brain/llm/__init__.py +37 -0
- core/api/services/brain/llm/_runner.py +62 -0
- core/api/services/brain/llm/base.py +70 -0
- core/api/services/brain/llm/cache.py +99 -0
- core/api/services/brain/llm/constants.py +46 -0
- core/api/services/brain/llm/direction_alignment.py +289 -0
- core/api/services/brain/llm/factory.py +132 -0
- core/api/services/brain/llm/finding_reasoning.py +98 -0
- core/api/services/brain/llm/finding_summary.py +92 -0
- core/api/services/brain/llm/grounding.py +46 -0
- core/api/services/brain/llm/journal_polish.py +96 -0
- core/api/services/brain/llm/local_gateway.py +426 -0
- core/api/services/brain/llm/parsers.py +71 -0
- core/api/services/brain/llm/router_glue.py +422 -0
- core/api/services/brain/memory_ops.py +1677 -0
- core/api/services/brain/models.py +140 -0
- core/api/services/brain/owner_hint.py +211 -0
- core/api/services/brain/recap.py +307 -0
- core/api/services/brain/rules/__init__.py +65 -0
- core/api/services/brain/rules/_signals.py +205 -0
- core/api/services/brain/rules/dr1_activity_without_status.py +108 -0
- core/api/services/brain/rules/dr2_decision_without_adr.py +110 -0
- core/api/services/brain/rules/dr3_stale_open_loop.py +127 -0
- core/api/services/brain/rules/dr4_docs_governance_drift.py +79 -0
- core/api/services/brain/rules/dr5_playbook_changed.py +99 -0
- core/api/services/brain/rules/dr6_external_update_unpropagated.py +100 -0
- core/api/services/brain/rules/dr7_claimed_decision_gap.py +123 -0
- core/api/services/brain/rules/dr8_direction_misalignment.py +230 -0
- core/api/services/brain/runs_reader.py +485 -0
- core/api/services/brain/scope.py +79 -0
- core/api/services/brain/sources/__init__.py +46 -0
- core/api/services/brain/sources/base.py +86 -0
- core/api/services/brain/sources/git_kg.py +393 -0
- core/api/services/brain/sources/handoffs.py +157 -0
- core/api/services/brain/sources/ingestor.py +130 -0
- core/api/services/brain/sources/learnings.py +121 -0
- core/api/services/brain/sources/pir_tasks.py +245 -0
- core/api/services/brain/watermarks.py +147 -0
- core/api/services/brain/ws_emitter.py +170 -0
- core/api/services/cc_tasks_reader.py +76 -0
- core/api/services/ci_service.py +263 -0
- core/api/services/claude_metrics.py +796 -0
- core/api/services/codex_metrics.py +364 -0
- core/api/services/conversation_reader.py +102 -0
- core/api/services/cost_service.py +243 -0
- core/api/services/crypto.py +147 -0
- core/api/services/docs_governance/__init__.py +1 -0
- core/api/services/docs_governance/confidence.py +230 -0
- core/api/services/docs_governance/config.py +87 -0
- core/api/services/docs_governance/enrichment.py +65 -0
- core/api/services/docs_governance/frontmatter_validator.py +83 -0
- core/api/services/docs_governance/hard_gates.py +221 -0
- core/api/services/docs_governance/triage_orchestrator.py +98 -0
- core/api/services/embedding_internal.py +395 -0
- core/api/services/embedding_service.py +832 -0
- core/api/services/event_dispatcher.py +167 -0
- core/api/services/events.py +70 -0
- core/api/services/git_ops.py +621 -0
- core/api/services/graph_cosmo_service.py +440 -0
- core/api/services/graph_ranker.py +306 -0
- core/api/services/graph_service.py +1589 -0
- core/api/services/inbox.py +800 -0
- core/api/services/inbox_digest.py +221 -0
- core/api/services/inbox_digest_deep_research.py +80 -0
- core/api/services/inbox_digest_jobs.py +595 -0
- core/api/services/inbox_gmail_sync.py +167 -0
- core/api/services/inbox_llm_classifier.py +906 -0
- core/api/services/inbox_source_identity.py +116 -0
- core/api/services/inbox_sources.py +456 -0
- core/api/services/inbox_taxonomy.py +195 -0
- core/api/services/inbox_tldr.py +1079 -0
- core/api/services/inbox_triage.py +899 -0
- core/api/services/ingest/__init__.py +13 -0
- core/api/services/ingest/api_key_auth.py +136 -0
- core/api/services/ingest/auto_approve.py +120 -0
- core/api/services/ingest/classifier.py +138 -0
- core/api/services/ingest/confidence.py +173 -0
- core/api/services/ingest/dispatch.py +88 -0
- core/api/services/ingest/embedding_router.py +272 -0
- core/api/services/ingest/events.py +33 -0
- core/api/services/ingest/ignore_patterns.py +79 -0
- core/api/services/ingest/image_probe.py +218 -0
- core/api/services/ingest/ingress.py +263 -0
- core/api/services/ingest/insert_saga.py +793 -0
- core/api/services/ingest/llm/__init__.py +13 -0
- core/api/services/ingest/llm/anthropic_haiku.py +23 -0
- core/api/services/ingest/llm/base.py +59 -0
- core/api/services/ingest/llm/byok_provider.py +130 -0
- core/api/services/ingest/llm/classification_context.py +301 -0
- core/api/services/ingest/llm/config_store.py +246 -0
- core/api/services/ingest/llm/factory.py +24 -0
- core/api/services/ingest/llm/kg_enricher.py +306 -0
- core/api/services/ingest/llm/local_gateway.py +821 -0
- core/api/services/ingest/llm/local_vllm.py +23 -0
- core/api/services/ingest/llm/openai_nano.py +349 -0
- core/api/services/ingest/lock_advisory.py +57 -0
- core/api/services/ingest/parser_router.py +1756 -0
- core/api/services/ingest/parsers/__init__.py +1 -0
- core/api/services/ingest/parsers/docling_parser.py +142 -0
- core/api/services/ingest/parsers/docparse_gateway.py +178 -0
- core/api/services/ingest/parsers/docx_parser.py +127 -0
- core/api/services/ingest/parsers/folder_unpacker.py +85 -0
- core/api/services/ingest/parsers/gateway_aux.py +147 -0
- core/api/services/ingest/parsers/image_parser.py +251 -0
- core/api/services/ingest/parsers/internal_markdown.py +89 -0
- core/api/services/ingest/parsers/ocr_gateway.py +117 -0
- core/api/services/ingest/parsers/ocr_pdf_parser.py +112 -0
- core/api/services/ingest/parsers/pdf_types.py +13 -0
- core/api/services/ingest/parsers/transcript_parser.py +445 -0
- core/api/services/ingest/parsers/vision_gateway.py +186 -0
- core/api/services/ingest/parsers/xlsx_parser.py +91 -0
- core/api/services/ingest/parsers/zip_unpacker.py +126 -0
- core/api/services/ingest/preflight.py +393 -0
- core/api/services/ingest/retry_voyage.py +88 -0
- core/api/services/ingest/routing_policy.py +307 -0
- core/api/services/ingest/serializers/__init__.py +1 -0
- core/api/services/ingest/serializers/xlsx_to_markdown.py +80 -0
- core/api/services/ingest/skip_log.py +74 -0
- core/api/services/ingest/watcher.py +637 -0
- core/api/services/kg/__init__.py +0 -0
- core/api/services/kg/audit.py +49 -0
- core/api/services/kg/hybrid_search.py +691 -0
- core/api/services/kg/lens.py +339 -0
- core/api/services/kg/pr_impact.py +770 -0
- core/api/services/kg/queries.py +152 -0
- core/api/services/kg/ranking.py +89 -0
- core/api/services/kg/rrf.py +143 -0
- core/api/services/kg_watcher_control.py +161 -0
- core/api/services/local_llm/__init__.py +19 -0
- core/api/services/local_llm/async_client.py +385 -0
- core/api/services/local_llm/client.py +173 -0
- core/api/services/local_llm/url_validator.py +44 -0
- core/api/services/metrics_collector.py +646 -0
- core/api/services/metrics_providers.py +65 -0
- core/api/services/model_registry.py +266 -0
- core/api/services/model_router.py +137 -0
- core/api/services/n8n_client.py +77 -0
- core/api/services/newsletter_llm_gateway.py +66 -0
- core/api/services/notification_service.py +134 -0
- core/api/services/openai_responses.py +55 -0
- core/api/services/opencode_metrics.py +375 -0
- core/api/services/opencode_sessions.py +173 -0
- core/api/services/pii_redactor.py +138 -0
- core/api/services/pr_impact_pipeline/__init__.py +21 -0
- core/api/services/pr_impact_pipeline/differ.py +421 -0
- core/api/services/pr_impact_pipeline/dispatcher.py +415 -0
- core/api/services/pr_impact_pipeline/gc.py +93 -0
- core/api/services/pr_impact_pipeline/languages.py +192 -0
- core/api/services/pr_impact_pipeline/parser.py +178 -0
- core/api/services/pr_impact_pipeline/writer.py +394 -0
- core/api/services/pr_service.py +1393 -0
- core/api/services/project_paths.py +70 -0
- core/api/services/project_status_updates.py +265 -0
- core/api/services/providers.py +276 -0
- core/api/services/push_service.py +170 -0
- core/api/services/reminder_service.py +89 -0
- core/api/services/runas.py +41 -0
- core/api/services/salience_service.py +69 -0
- core/api/services/security_collector.py +281 -0
- core/api/services/session_catalog.py +385 -0
- core/api/services/session_metrics_service.py +301 -0
- core/api/services/session_ops.py +272 -0
- core/api/services/session_state.py +173 -0
- core/api/services/share_links.py +222 -0
- core/api/services/task_transitions.py +146 -0
- core/api/services/terminal_metrics.py +462 -0
- core/api/services/terminal_metrics_dump.py +203 -0
- core/api/services/tmux.py +1205 -0
- core/api/services/webhook_service.py +422 -0
- core/api/services/workspace_sync.py +164 -0
- core/api/templates/__init__.py +1 -0
- core/api/templates/markdown_share.py +164 -0
- core/api/terminal.py +1031 -0
- core/api/tests/__init__.py +0 -0
- core/api/tests/test_agent_facing_auth_dependencies.py +132 -0
- core/api/tests/test_audit_permissions.py +133 -0
- core/api/tests/test_backfill_session_conversations.py +90 -0
- core/api/tests/test_backfill_working_seconds_msg.py +129 -0
- core/api/tests/test_claude_metrics.py +326 -0
- core/api/tests/test_codex_metrics.py +189 -0
- core/api/tests/test_finder_paths.py +74 -0
- core/api/tests/test_git_ops_merge.py +155 -0
- core/api/tests/test_learnings_check_search.py +81 -0
- core/api/tests/test_metrics_providers.py +133 -0
- core/api/tests/test_migration_087.py +164 -0
- core/api/tests/test_migration_088.py +94 -0
- core/api/tests/test_migration_089.py +116 -0
- core/api/tests/test_openai_responses.py +24 -0
- core/api/tests/test_opencode_metrics.py +740 -0
- core/api/tests/test_opencode_sessions.py +321 -0
- core/api/tests/test_pr_workflow_e2e.py +457 -0
- core/api/tests/test_projects_handoffs.py +31 -0
- core/api/tests/test_providers.py +138 -0
- core/api/tests/test_safety_bridge.py +347 -0
- core/api/tests/test_session_catalog.py +142 -0
- core/api/tests/test_session_conversations.py +512 -0
- core/api/tests/test_session_metrics_service.py +270 -0
- core/api/tests/test_session_resume_paths.py +548 -0
- core/api/tests/test_session_theme_mode_migration.py +56 -0
- core/api/tests/test_sessions_rbac.py +131 -0
- core/api/tests/test_share_edit.py +398 -0
- core/api/tests/test_share_repo.py +200 -0
- core/api/tests/test_terminal_session_manager.py +98 -0
- core/api/tests/test_terminal_upload.py +34 -0
- core/api/tests/test_tmux.py +272 -0
- core/api/tests/test_workspace_sync.py +186 -0
- core/api/tests/test_ws_ticket_in_memory.py +73 -0
- core/api/use_cases/__init__.py +11 -0
- core/api/use_cases/_context.py +89 -0
- core/api/use_cases/_errors.py +62 -0
- core/api/use_cases/_roles.py +16 -0
- core/api/use_cases/audit.py +171 -0
- core/api/use_cases/brain.py +1232 -0
- core/api/use_cases/costs.py +249 -0
- core/api/use_cases/graph.py +1153 -0
- core/api/use_cases/handoffs.py +506 -0
- core/api/use_cases/ingest_triage.py +1229 -0
- core/api/use_cases/learnings.py +538 -0
- core/api/use_cases/projects.py +705 -0
- core/api/use_cases/pull_requests.py +415 -0
- core/api/use_cases/search.py +926 -0
- core/api/use_cases/tasks.py +1495 -0
- core/api/visibility.py +141 -0
- core/cli/__init__.py +5 -0
- core/cli/_index_source.py +632 -0
- core/cli/_runtime_ctx.py +160 -0
- core/cli/_transmute.py +241 -0
- core/cli/marvis_doctor.py +704 -0
- core/cli/marvis_feedback.py +396 -0
- core/cli/marvis_governance.py +315 -0
- core/cli/marvis_hooks.py +515 -0
- core/cli/marvis_init.py +757 -0
- core/cli/marvis_mcp.py +401 -0
- core/cli/marvis_runtime.py +855 -0
- core/cli/marvis_telemetry.py +228 -0
- core/scripts/_drift_check.py +716 -0
- core/scripts/_frontmatter.py +66 -0
- core/scripts/_graph_writer.py +189 -0
- core/scripts/ast_parser.py +1553 -0
- core/scripts/install_hooks/__init__.py +1 -0
- core/scripts/install_hooks/_config.sh +109 -0
- core/scripts/install_hooks/block-dangerous-bash.sh +23 -0
- core/scripts/install_hooks/block-db-direct-write.sh +23 -0
- core/scripts/install_hooks/block-push-no-task.sh +23 -0
- core/scripts/install_hooks/block-staging-to-prod.sh +23 -0
- core/scripts/install_hooks/block-subtree-push.sh +23 -0
- core/scripts/install_hooks/config.json +53 -0
- core/scripts/install_hooks/enforce-no-merge-main.sh +23 -0
- core/scripts/install_hooks/enforce-worktree.sh +23 -0
- core/scripts/install_hooks/quality-gate.sh +170 -0
- core/scripts/install_hooks/safety_bridge.py +968 -0
- core/scripts/install_hooks/secret-scan.sh +23 -0
- core/scripts/migrate_spike_node_ids.py +122 -0
- core/scripts/populate_artifacts.py +2198 -0
- core/scripts/populate_cross_project.py +2457 -0
- core/scripts/populate_inbox_nodes.py +357 -0
- core/scripts/populate_pr_impact.py +267 -0
- core/scripts/populate_project_nodes.py +603 -0
- core/scripts/populate_touch_counter.py +337 -0
- core/scripts/reparse_failed.py +57 -0
- core/scripts/safety_bridge.py +968 -0
- core/telemetry/__init__.py +9 -0
- core/telemetry/client.py +405 -0
- core/telemetry/schema.py +122 -0
- core/wizard/__init__.py +65 -0
- core/wizard/byok_vault.py +147 -0
- core/wizard/defaults.py +58 -0
- core/wizard/state.py +117 -0
- core/wizard/steps.py +70 -0
- core/wizard/validation.py +136 -0
- marvisx_cli-0.1.0.dist-info/METADATA +201 -0
- marvisx_cli-0.1.0.dist-info/RECORD +587 -0
- marvisx_cli-0.1.0.dist-info/WHEEL +5 -0
- marvisx_cli-0.1.0.dist-info/entry_points.txt +3 -0
- marvisx_cli-0.1.0.dist-info/licenses/LICENSE +98 -0
- marvisx_cli-0.1.0.dist-info/top_level.txt +3 -0
- migrations/001_initial.sql +33 -0
- migrations/002_tasks.sql +30 -0
- migrations/003_session_management.sql +7 -0
- migrations/004_projects_comments.sql +65 -0
- migrations/005_session_intelligence.sql +15 -0
- migrations/006_settings.sql +12 -0
- migrations/007_task_scoring.sql +12 -0
- migrations/008_cost_tracking.sql +31 -0
- migrations/009_session_card_metrics.sql +3 -0
- migrations/010_monitoring.sql +55 -0
- migrations/012_agent_api.sql +21 -0
- migrations/013_session_complete.sql +8 -0
- migrations/015_pull_requests.sql +43 -0
- migrations/015_pull_requests_down.sql +5 -0
- migrations/016_users_raci.sql +116 -0
- migrations/017_task_cost_entries.sql +87 -0
- migrations/018_agents.sql +73 -0
- migrations/018_agents_down.sql +13 -0
- migrations/019_review_feedback.sql +18 -0
- migrations/020_pr_commit_sha.sql +4 -0
- migrations/021_webhook_events.sql +18 -0
- migrations/022_devx_agent_managed.sql +11 -0
- migrations/022_devx_agent_managed_down.sql +6 -0
- migrations/023_devx_p1_gate.sql +7 -0
- migrations/023_devx_p1_gate_down.sql +3 -0
- migrations/024_chat_messages.sql +16 -0
- migrations/024_pr_conversation_id.sql +8 -0
- migrations/024_task_indexes.sql +21 -0
- migrations/024_task_indexes_down.sql +7 -0
- migrations/025_audit_log.sql +17 -0
- migrations/026_agent_tokens.sql +20 -0
- migrations/027_teams_auth_phase_b.sql +35 -0
- migrations/028_learnings.sql +23 -0
- migrations/029_team_roles.sql +14 -0
- migrations/030_finder_pins.sql +10 -0
- migrations/031_pr_deploy_status.sql +9 -0
- migrations/032_task_reminders.sql +7 -0
- migrations/033_events_retry_count.sql +6 -0
- migrations/033_session_owner.sql +9 -0
- migrations/034_notifications.sql +38 -0
- migrations/035_shared_links.sql +15 -0
- migrations/036_session_index_upgrade.sql +29 -0
- migrations/037_pr_approval.sql +15 -0
- migrations/038_pr_submitted_by.sql +6 -0
- migrations/039_push_subscriptions.sql +17 -0
- migrations/040_semantic_search.sql +16 -0
- migrations/041_workspaces.sql +63 -0
- migrations/042_oidc_providers.sql +24 -0
- migrations/043_ci_checks.sql +31 -0
- migrations/044_agent_metrics.sql +30 -0
- migrations/045_documents_doc_type.sql +5 -0
- migrations/046_salience.sql +13 -0
- migrations/047_seed_missing_agents.sql +6 -0
- migrations/048_fix_agent_paths_roles.sql +5 -0
- migrations/049_agent_role_and_learnings_schema.sql +3 -0
- migrations/050_session_provider.sql +2 -0
- migrations/051_session_launch_profile.sql +4 -0
- migrations/052_session_theme_mode.sql +2 -0
- migrations/052_task_kind.sql +4 -0
- migrations/053_inbox_items.sql +31 -0
- migrations/054_inbox_triage_contract.sql +30 -0
- migrations/055_inbox_topic_treatment.sql +12 -0
- migrations/056_inbox_treatment_read_save.sql +57 -0
- migrations/057_session_theme_mode_backfill.sql +4 -0
- migrations/058_inbox_item_status_lifecycle.sql +13 -0
- migrations/059_inbox_tldr_and_source_scores.sql +18 -0
- migrations/060_newsletter.sql +16 -0
- migrations/061_inbox_redesign.sql +69 -0
- migrations/062_fix_inbox_sources_backfill.sql +37 -0
- migrations/063_task_completion_mode.sql +23 -0
- migrations/064_judge_mode_setting.sql +4 -0
- migrations/065_knowledge_graph_spike.sql +40 -0
- migrations/066_digest_ranking_inputs.sql +10 -0
- migrations/066_kg_artifact_nodes.sql +129 -0
- migrations/067_inbox_digest_selections.sql +28 -0
- migrations/067_kg_temporal.sql +53 -0
- migrations/068_inbox_digest_app_settings.sql +9 -0
- migrations/068_kg_touch_counter.sql +52 -0
- migrations/069_kg_doc_types.sql +117 -0
- migrations/070_digest_ranking_inputs_recovery.sql +3 -0
- migrations/071_inbox_digest_selections_recovery.sql +3 -0
- migrations/072_inbox_digest_app_settings_recovery.sql +3 -0
- migrations/073_kg_cross_project.sql +216 -0
- migrations/073_kg_cross_project_down.sql +77 -0
- migrations/074_kg_infra_types.sql +208 -0
- migrations/074_kg_infra_types_down.sql +80 -0
- migrations/075_kg_file_state_recovery.sql +35 -0
- migrations/075_kg_file_state_recovery_down.sql +5 -0
- migrations/076_kg_watcher_state.sql +33 -0
- migrations/076_kg_watcher_state_down.sql +3 -0
- migrations/077_kg_doc_types_extend.sql +226 -0
- migrations/077_kg_doc_types_extend_down.sql +80 -0
- migrations/078_kg_fts5.sql +102 -0
- migrations/078_kg_fts5_down.sql +14 -0
- migrations/079_kg_missing_indexes.sql +31 -0
- migrations/079_kg_missing_indexes_down.sql +10 -0
- migrations/080_kg_fts5_extended.sql +232 -0
- migrations/080_kg_fts5_extended_down.sql +25 -0
- migrations/081_kg_lens_indexes.sql +9 -0
- migrations/081_kg_lens_indexes_down.sql +3 -0
- migrations/082_kg_pins.sql +26 -0
- migrations/082_kg_pins_down.sql +14 -0
- migrations/083_kg_graph_nodes_degree.sql +20 -0
- migrations/083_kg_graph_nodes_degree_down.sql +15 -0
- migrations/084_drop_legacy_scheduler_tables.sql +58 -0
- migrations/084_drop_legacy_scheduler_tables_down.sql +112 -0
- migrations/085_kg_edge_resolves_to.sql +142 -0
- migrations/085_kg_edge_resolves_to_down.sql +66 -0
- migrations/086_project_status_updates_feed.sql +20 -0
- migrations/086_project_status_updates_feed_down.sql +36 -0
- migrations/087_session_metrics_dual.sql +50 -0
- migrations/087_session_metrics_dual_down.sql +21 -0
- migrations/088_rename_context_pct_legacy.sql +23 -0
- migrations/088_rename_context_pct_legacy_down.sql +8 -0
- migrations/089_session_metrics_equivalent_cost.sql +26 -0
- migrations/089_session_metrics_equivalent_cost_down.sql +11 -0
- migrations/090_kg_inbox_node_type.sql +26 -0
- migrations/090_kg_inbox_node_type_down.sql +20 -0
- migrations/091_kg_inbox_node_type_check.sql +265 -0
- migrations/091_kg_inbox_node_type_check_down.sql +129 -0
- migrations/092_sessions_activity_state_ts.sql +29 -0
- migrations/092_sessions_activity_state_ts_down.sql +14 -0
- migrations/093_sessions_activity_state_column.sql +29 -0
- migrations/093_sessions_activity_state_column_down.sql +10 -0
- migrations/094_ingest_pending.sql +55 -0
- migrations/094_ingest_pending_down.sql +15 -0
- migrations/095_kg_intent_first.sql +77 -0
- migrations/095_kg_intent_first_down.sql +25 -0
- migrations/096_kg_xlsx_artifact_prefix.sql +17 -0
- migrations/096_kg_xlsx_artifact_prefix_down.sql +11 -0
- migrations/097_ingest_change_history.sql +37 -0
- migrations/097_ingest_change_history_down.sql +13 -0
- migrations/098_kg_node_type_business.sql +254 -0
- migrations/098_kg_node_type_business_down.sql +195 -0
- migrations/099_kg_edges_restore_weight.sql +58 -0
- migrations/099_kg_edges_restore_weight_down.sql +12 -0
- migrations/100_kg_enriched_at.sql +25 -0
- migrations/100_kg_enriched_at_down.sql +12 -0
- migrations/101_local_llm_shadow_comparisons.sql +66 -0
- migrations/101_local_llm_shadow_comparisons_down.sql +15 -0
- migrations/102_promote_llm_costs.sql +69 -0
- migrations/102_promote_llm_costs_down.sql +19 -0
- migrations/103_ingest_skipped_log.sql +46 -0
- migrations/103_ingest_skipped_log_down.sql +15 -0
- migrations/120_docs_governance.sql +50 -0
- migrations/120_docs_governance_down.sql +11 -0
- migrations/121_notification_event_fk_cleanup.sql +21 -0
- migrations/121_notification_event_fk_cleanup_down.sql +10 -0
- migrations/122_docs_drift_history.sql +34 -0
- migrations/122_docs_drift_history_down.sql +15 -0
- migrations/123_ingest_parser_waiting_status.sql +69 -0
- migrations/123_ingest_parser_waiting_status_down.sql +69 -0
- migrations/124_heypocket_recordings.sql +63 -0
- migrations/124_heypocket_recordings_down.sql +13 -0
- migrations/125_kg_node_type_record.sql +219 -0
- migrations/125_kg_node_type_record_down.sql +205 -0
- migrations/126_ingest_terminal_upload_source_kind.sql +69 -0
- migrations/126_ingest_terminal_upload_source_kind_down.sql +69 -0
- migrations/127_brain_v1_substrate.sql +200 -0
- migrations/127_brain_v1_substrate_down.sql +32 -0
- migrations/128_brain_drift_signals.sql +157 -0
- migrations/128_brain_drift_signals_down.sql +23 -0
- migrations/129_brain_memory_operations.sql +232 -0
- migrations/129_brain_memory_operations_down.sql +27 -0
- migrations/130_brain_findings.sql +258 -0
- migrations/130_brain_findings_down.sql +29 -0
- migrations/132_kg_pr_modifies.sql +242 -0
- migrations/132_kg_pr_modifies_down.sql +99 -0
- migrations/133_brain_v1_2_direction_schema.sql +476 -0
- migrations/133_brain_v1_2_direction_schema_down.sql +273 -0
- migrations/134_brain_journal_narrative_polished.sql +8 -0
- migrations/134_brain_journal_narrative_polished_down.sql +6 -0
- migrations/135_kg_edges_provider.sql +21 -0
- migrations/136_documents_fts.sql +56 -0
- migrations/137_promote_llm_costs.sql +59 -0
- migrations/137_promote_llm_costs_down.sql +19 -0
- migrations/138_ingest_api_keys.sql +39 -0
- migrations/138_ingest_api_keys_down.sql +8 -0
- migrations/139_ingest_pending_ingress.sql +91 -0
- migrations/139_ingest_pending_ingress_down.sql +73 -0
- migrations/140_ingest_idempotency_quota.sql +45 -0
- migrations/140_ingest_idempotency_quota_down.sql +9 -0
- migrations/141_ingest_pending_metadata.sql +16 -0
- migrations/141_ingest_pending_metadata_down.sql +7 -0
- migrations/142_llm_function_config.sql +36 -0
- migrations/142_llm_function_config_down.sql +8 -0
- migrations/143_kg_code_embeddings.sql +25 -0
- migrations/143_kg_code_embeddings_down.sql +5 -0
- migrations/__init__.py +4 -0
- projects/_template/project.yaml +46 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# v2.0.0 - 2026-05-27 - S1 F1.1: thin adapter over use_cases.learnings (TEMPLATE router)
|
|
2
|
+
# v1.1.0 - 2026-04-16 - Phase 7.0 KG Inline Lens: ?deep=true on get/list/check + H4 visibility guard
|
|
3
|
+
"""HTTP adapter for the learnings domain (S1 collapse-runtime TEMPLATE).
|
|
4
|
+
|
|
5
|
+
This router is a thin transport adapter. All CRUD/search/validation/RBAC/
|
|
6
|
+
visibility logic lives in :mod:`core.api.use_cases.learnings` (pure, fastapi-free).
|
|
7
|
+
Each handler:
|
|
8
|
+
|
|
9
|
+
1. resolves identity into a :class:`CallerContext` (``from_user_info``; learnings
|
|
10
|
+
has no human-only gate, so ``is_human_session=False`` — no ``Request`` needed);
|
|
11
|
+
2. for ``list``, resolves visibility at the boundary (DECISION 1) and passes it in;
|
|
12
|
+
3. calls the use_case inside ``try/except ServiceError`` -> ``to_http``;
|
|
13
|
+
4. owns everything ``deep``-related: the ``deep_requires_filter`` 400 guard
|
|
14
|
+
(DECISION 3) and the rate-limit + audit + ``kg_context`` enrichment (DECISION 2).
|
|
15
|
+
|
|
16
|
+
The pure helpers and DTOs are re-exported from the use_case so existing importers
|
|
17
|
+
(``main.py`` for ``router``; ``services``/tests for the helpers) keep working.
|
|
18
|
+
"""
|
|
19
|
+
from __future__ import annotations
|
|
20
|
+
|
|
21
|
+
import asyncio
|
|
22
|
+
import logging
|
|
23
|
+
|
|
24
|
+
import aiosqlite
|
|
25
|
+
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
26
|
+
from pydantic import BaseModel, Field
|
|
27
|
+
|
|
28
|
+
from core.api.db import get_db, get_write_db
|
|
29
|
+
from core.api.models import UserInfo
|
|
30
|
+
from core.api.rbac import require_role
|
|
31
|
+
from core.api.routers._adapter import to_http
|
|
32
|
+
from core.api.security import get_current_user_or_agent
|
|
33
|
+
from core.api.services.kg.audit import check_deep_rate_limit, log_kg_deep_access
|
|
34
|
+
from core.api.services.kg.lens import build_kg_context_for_learning
|
|
35
|
+
from core.api.use_cases import learnings as uc
|
|
36
|
+
from core.api.use_cases._context import CallerContext
|
|
37
|
+
from core.api.use_cases._errors import ServiceError
|
|
38
|
+
from core.api.visibility import get_visible_projects
|
|
39
|
+
|
|
40
|
+
# Re-export the domain DTOs + pure helpers + constants from the use_case so that
|
|
41
|
+
# (a) `response_model=` below references the same classes and (b) existing
|
|
42
|
+
# importers — `services/kg/lens.py`, `core/api/tests/test_learnings_check_search.py`
|
|
43
|
+
# — keep importing them from this router path unchanged.
|
|
44
|
+
from core.api.use_cases.learnings import ( # noqa: F401 (re-export surface)
|
|
45
|
+
CHECK_LEARNINGS_STOPWORDS,
|
|
46
|
+
VALID_CATEGORIES,
|
|
47
|
+
VALID_SEVERITIES,
|
|
48
|
+
LearningCheckResponse,
|
|
49
|
+
LearningResponse,
|
|
50
|
+
_extract_check_terms,
|
|
51
|
+
_learning_match_score,
|
|
52
|
+
_row_to_learning,
|
|
53
|
+
_search_learning_rows,
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
logger = logging.getLogger(__name__)
|
|
57
|
+
|
|
58
|
+
router = APIRouter(prefix="/api/v1/learnings", tags=["learnings"])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
# ---------------------------------------------------------------------------
|
|
62
|
+
# HTTP request models (transport contract — these STAY in the router)
|
|
63
|
+
# ---------------------------------------------------------------------------
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
class LearningCreateRequest(BaseModel):
|
|
67
|
+
title: str
|
|
68
|
+
category: str
|
|
69
|
+
description: str
|
|
70
|
+
tags: list[str] = Field(default_factory=list)
|
|
71
|
+
module: str | None = None
|
|
72
|
+
severity: str = "medium"
|
|
73
|
+
prevention: str | None = None
|
|
74
|
+
session: int | None = None
|
|
75
|
+
project: str | None = None
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class LearningUpdateRequest(BaseModel):
|
|
79
|
+
title: str | None = None
|
|
80
|
+
category: str | None = None
|
|
81
|
+
description: str | None = None
|
|
82
|
+
tags: list[str] | None = None
|
|
83
|
+
module: str | None = None
|
|
84
|
+
severity: str | None = None
|
|
85
|
+
prevention: str | None = None
|
|
86
|
+
session: int | None = None
|
|
87
|
+
project: str | None = None
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
# ---------------------------------------------------------------------------
|
|
91
|
+
# Endpoints (thin adapters)
|
|
92
|
+
# ---------------------------------------------------------------------------
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
@router.get("/check", response_model=LearningCheckResponse)
|
|
96
|
+
async def check_learnings(
|
|
97
|
+
q: str = Query(
|
|
98
|
+
..., description="Keyword to search in title, description, tags, module"
|
|
99
|
+
),
|
|
100
|
+
module: str | None = Query(
|
|
101
|
+
None, description="Filter by module (exact or LIKE match)"
|
|
102
|
+
),
|
|
103
|
+
deep: bool = Query(False, description="Attach KG context to each result (Phase 7.0)"),
|
|
104
|
+
user: UserInfo = Depends(get_current_user_or_agent),
|
|
105
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
106
|
+
) -> LearningCheckResponse:
|
|
107
|
+
"""Search learnings relevant to a keyword/module. Used by MCP before risky actions."""
|
|
108
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
109
|
+
try:
|
|
110
|
+
result = await uc.check_learnings(ctx, db, query=q, module=module)
|
|
111
|
+
except ServiceError as e:
|
|
112
|
+
raise to_http(e)
|
|
113
|
+
|
|
114
|
+
# DECISION 2: deep KG enrichment is a per-surface adapter concern.
|
|
115
|
+
if deep and result.results:
|
|
116
|
+
check_deep_rate_limit(user.username)
|
|
117
|
+
log_kg_deep_access(user.username, "check_learnings", q)
|
|
118
|
+
kg_contexts = await asyncio.gather(
|
|
119
|
+
*[build_kg_context_for_learning(db, r.id, deep=True) for r in result.results],
|
|
120
|
+
return_exceptions=True,
|
|
121
|
+
)
|
|
122
|
+
for r, kg_ctx in zip(result.results, kg_contexts):
|
|
123
|
+
if not isinstance(kg_ctx, Exception):
|
|
124
|
+
r.kg_context = kg_ctx
|
|
125
|
+
|
|
126
|
+
return result
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
@router.get("", response_model=list[LearningResponse])
|
|
130
|
+
async def list_learnings(
|
|
131
|
+
category: str | None = None,
|
|
132
|
+
project: str | None = None,
|
|
133
|
+
severity: str | None = None,
|
|
134
|
+
tags: str | None = Query(None, description="Comma-separated tags (OR logic)"),
|
|
135
|
+
search: str | None = Query(None, description="Free text search"),
|
|
136
|
+
module: str | None = Query(None, description="Filter by module"),
|
|
137
|
+
limit: int = Query(50, ge=1, le=200),
|
|
138
|
+
offset: int = Query(0, ge=0),
|
|
139
|
+
deep: bool = Query(False, description="Attach KG context (aggregate deferred to Phase 7.1)"),
|
|
140
|
+
user: UserInfo = Depends(get_current_user_or_agent),
|
|
141
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
142
|
+
) -> list[LearningResponse]:
|
|
143
|
+
"""List learnings with filters."""
|
|
144
|
+
# DECISION 3: deep_requires_filter is an adapter-owned guard tied to the
|
|
145
|
+
# adapter-owned `deep` feature. Keep the exact 400 (NOT via ServiceError).
|
|
146
|
+
if deep and not project and not module and not search:
|
|
147
|
+
raise HTTPException(
|
|
148
|
+
status_code=400,
|
|
149
|
+
detail="deep=true requires ?project=, ?module=, or ?search= filter",
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
# DECISION 1 (the visibility template): resolve visibility at the boundary
|
|
153
|
+
# (needs UserInfo.teams, not carried by CallerContext) and pass it in; the
|
|
154
|
+
# use_case enforces it. Only resolve when a project filter is present.
|
|
155
|
+
visible_projects = await get_visible_projects(db, user) if project else None
|
|
156
|
+
|
|
157
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
158
|
+
try:
|
|
159
|
+
return await uc.list_learnings(
|
|
160
|
+
ctx,
|
|
161
|
+
db,
|
|
162
|
+
category=category,
|
|
163
|
+
project=project,
|
|
164
|
+
severity=severity,
|
|
165
|
+
tags=tags,
|
|
166
|
+
search=search,
|
|
167
|
+
module=module,
|
|
168
|
+
limit=limit,
|
|
169
|
+
offset=offset,
|
|
170
|
+
visible_projects=visible_projects,
|
|
171
|
+
)
|
|
172
|
+
except ServiceError as e:
|
|
173
|
+
raise to_http(e)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
@router.get("/{learning_id}", response_model=LearningResponse)
|
|
177
|
+
async def get_learning(
|
|
178
|
+
learning_id: str,
|
|
179
|
+
deep: bool = Query(False, description="Attach KG context (Phase 7.0)"),
|
|
180
|
+
user: UserInfo = Depends(get_current_user_or_agent),
|
|
181
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
182
|
+
) -> LearningResponse:
|
|
183
|
+
"""Get a single learning by ID."""
|
|
184
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
185
|
+
try:
|
|
186
|
+
result = await uc.get_learning(ctx, db, learning_id=learning_id)
|
|
187
|
+
except ServiceError as e:
|
|
188
|
+
raise to_http(e)
|
|
189
|
+
|
|
190
|
+
# DECISION 2: deep KG enrichment is a per-surface adapter concern.
|
|
191
|
+
if deep:
|
|
192
|
+
check_deep_rate_limit(user.username)
|
|
193
|
+
log_kg_deep_access(user.username, "get_learning", learning_id)
|
|
194
|
+
result.kg_context = await build_kg_context_for_learning(db, learning_id, deep=True)
|
|
195
|
+
return result
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
@router.post("", response_model=LearningResponse, status_code=201)
|
|
199
|
+
async def create_learning(
|
|
200
|
+
body: LearningCreateRequest,
|
|
201
|
+
user: UserInfo = Depends(require_role("operator", "admin", "super_admin")),
|
|
202
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
203
|
+
) -> LearningResponse:
|
|
204
|
+
"""Create a new learning."""
|
|
205
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
206
|
+
try:
|
|
207
|
+
return await uc.create_learning(
|
|
208
|
+
ctx,
|
|
209
|
+
db,
|
|
210
|
+
title=body.title,
|
|
211
|
+
category=body.category,
|
|
212
|
+
description=body.description,
|
|
213
|
+
tags=body.tags,
|
|
214
|
+
module=body.module,
|
|
215
|
+
severity=body.severity,
|
|
216
|
+
prevention=body.prevention,
|
|
217
|
+
session=body.session,
|
|
218
|
+
project=body.project,
|
|
219
|
+
)
|
|
220
|
+
except ServiceError as e:
|
|
221
|
+
raise to_http(e)
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
@router.patch("/{learning_id}", response_model=LearningResponse)
|
|
225
|
+
async def update_learning(
|
|
226
|
+
learning_id: str,
|
|
227
|
+
body: LearningUpdateRequest,
|
|
228
|
+
user: UserInfo = Depends(require_role("operator", "admin", "super_admin")),
|
|
229
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
230
|
+
) -> LearningResponse:
|
|
231
|
+
"""Update a learning."""
|
|
232
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
233
|
+
# exclude_unset preserves the HTTP semantic (omitted = unchanged, null = clear),
|
|
234
|
+
# equivalent to the previous `body.model_fields_set` gating.
|
|
235
|
+
fields = body.model_dump(exclude_unset=True)
|
|
236
|
+
try:
|
|
237
|
+
return await uc.update_learning(ctx, db, learning_id=learning_id, fields=fields)
|
|
238
|
+
except ServiceError as e:
|
|
239
|
+
raise to_http(e)
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
@router.post("/{learning_id}/bump", response_model=LearningResponse)
|
|
243
|
+
async def bump_learning(
|
|
244
|
+
learning_id: str,
|
|
245
|
+
user: UserInfo = Depends(require_role("operator", "admin", "super_admin")),
|
|
246
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
247
|
+
) -> LearningResponse:
|
|
248
|
+
"""Increment frequency and update last_occurrence. Used when the same issue recurs."""
|
|
249
|
+
ctx = CallerContext.from_user_info(user, is_human_session=False)
|
|
250
|
+
try:
|
|
251
|
+
return await uc.bump_learning(ctx, db, learning_id=learning_id)
|
|
252
|
+
except ServiceError as e:
|
|
253
|
+
raise to_http(e)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# v1.0.0 - 2026-05-26 - M1 CAPTURE U5 — per-function LLM config + provider keys (admin)
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
import logging
|
|
5
|
+
|
|
6
|
+
import aiosqlite
|
|
7
|
+
from fastapi import APIRouter, Depends, HTTPException, Path as PathParam
|
|
8
|
+
|
|
9
|
+
from core.api.db import get_db, get_write_db
|
|
10
|
+
from core.api.models.llm_config import (
|
|
11
|
+
LLMFunction,
|
|
12
|
+
LLMFunctionConfigItem,
|
|
13
|
+
LLMFunctionConfigUpdate,
|
|
14
|
+
ProviderKeyCreateRequest,
|
|
15
|
+
ProviderKeyResponse,
|
|
16
|
+
)
|
|
17
|
+
from core.api.rbac import require_role
|
|
18
|
+
from core.api.services.ingest.llm import config_store
|
|
19
|
+
|
|
20
|
+
logger = logging.getLogger(__name__)
|
|
21
|
+
router = APIRouter(prefix="/api/v1/llm-config", tags=["llm-config"])
|
|
22
|
+
|
|
23
|
+
_FUNCTIONS = ("classify", "embedding", "brain")
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _ws(caller) -> str:
|
|
27
|
+
return getattr(caller, "workspace_id", None) or "ws_default"
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
@router.get("/status")
|
|
31
|
+
async def get_llm_status(
|
|
32
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
33
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
34
|
+
) -> dict[str, str]:
|
|
35
|
+
"""Auto-classify provider status, drives the Console 'configure a provider'
|
|
36
|
+
alert: 'configured' or 'disabled_no_provider'."""
|
|
37
|
+
return {"classify": await config_store.classify_provider_status(db, _ws(caller))}
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@router.get("", response_model=list[LLMFunctionConfigItem])
|
|
41
|
+
async def get_llm_config(
|
|
42
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
43
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
44
|
+
) -> list[LLMFunctionConfigItem]:
|
|
45
|
+
rows = await config_store.list_function_configs(db, _ws(caller))
|
|
46
|
+
return [LLMFunctionConfigItem(**row) for row in rows]
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@router.put("/{function_name}", response_model=LLMFunctionConfigItem)
|
|
50
|
+
async def put_llm_config(
|
|
51
|
+
body: LLMFunctionConfigUpdate,
|
|
52
|
+
function_name: LLMFunction = PathParam(...),
|
|
53
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
54
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
55
|
+
) -> LLMFunctionConfigItem:
|
|
56
|
+
if function_name not in _FUNCTIONS:
|
|
57
|
+
raise HTTPException(status_code=422, detail=f"unknown function {function_name!r}")
|
|
58
|
+
ws = _ws(caller)
|
|
59
|
+
if body.provider_key_id is not None:
|
|
60
|
+
async with db.execute(
|
|
61
|
+
"SELECT 1 FROM provider_keys WHERE id = ? AND workspace_id = ?",
|
|
62
|
+
(body.provider_key_id, ws),
|
|
63
|
+
) as cur:
|
|
64
|
+
if await cur.fetchone() is None:
|
|
65
|
+
raise HTTPException(status_code=404, detail="provider_key_id not found")
|
|
66
|
+
await config_store.set_function_config(
|
|
67
|
+
db,
|
|
68
|
+
function_name=function_name,
|
|
69
|
+
provider_key_id=body.provider_key_id,
|
|
70
|
+
model=body.model,
|
|
71
|
+
enabled=body.enabled,
|
|
72
|
+
workspace_id=ws,
|
|
73
|
+
)
|
|
74
|
+
await db.commit()
|
|
75
|
+
rows = await config_store.list_function_configs(db, ws)
|
|
76
|
+
item = next(r for r in rows if r["function_name"] == function_name)
|
|
77
|
+
return LLMFunctionConfigItem(**item)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
@router.get("/provider-keys", response_model=list[ProviderKeyResponse])
|
|
81
|
+
async def list_provider_keys(
|
|
82
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
83
|
+
db: aiosqlite.Connection = Depends(get_db),
|
|
84
|
+
) -> list[ProviderKeyResponse]:
|
|
85
|
+
rows = await config_store.list_provider_keys(db, _ws(caller))
|
|
86
|
+
return [ProviderKeyResponse(**row) for row in rows]
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
@router.post("/provider-keys", response_model=ProviderKeyResponse, status_code=201)
|
|
90
|
+
async def create_provider_key(
|
|
91
|
+
body: ProviderKeyCreateRequest,
|
|
92
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
93
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
94
|
+
) -> ProviderKeyResponse:
|
|
95
|
+
ws = _ws(caller)
|
|
96
|
+
try:
|
|
97
|
+
key_id = await config_store.create_provider_key(
|
|
98
|
+
db,
|
|
99
|
+
provider=body.provider,
|
|
100
|
+
label=body.label,
|
|
101
|
+
api_key=body.api_key,
|
|
102
|
+
base_url=body.base_url,
|
|
103
|
+
workspace_id=ws,
|
|
104
|
+
)
|
|
105
|
+
except ValueError as exc:
|
|
106
|
+
raise HTTPException(status_code=422, detail=str(exc))
|
|
107
|
+
except RuntimeError:
|
|
108
|
+
# crypto fail-closed (BYOK_FERNET_SECRET missing) — never echo internals.
|
|
109
|
+
logger.error("create_provider_key: encryption unavailable", exc_info=True)
|
|
110
|
+
raise HTTPException(
|
|
111
|
+
status_code=503,
|
|
112
|
+
detail="Provider key storage unavailable: server encryption is not configured.",
|
|
113
|
+
)
|
|
114
|
+
await db.commit()
|
|
115
|
+
rows = await config_store.list_provider_keys(db, ws)
|
|
116
|
+
row = next(r for r in rows if r["id"] == key_id)
|
|
117
|
+
return ProviderKeyResponse(**row)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@router.delete("/provider-keys/{key_id}", status_code=204)
|
|
121
|
+
async def delete_provider_key(
|
|
122
|
+
key_id: str,
|
|
123
|
+
caller=Depends(require_role("admin", "super_admin", human_only=True)),
|
|
124
|
+
db: aiosqlite.Connection = Depends(get_write_db),
|
|
125
|
+
) -> None:
|
|
126
|
+
ws = _ws(caller)
|
|
127
|
+
deleted = await config_store.delete_provider_key(db, key_id, ws)
|
|
128
|
+
await db.commit()
|
|
129
|
+
if not deleted:
|
|
130
|
+
raise HTTPException(status_code=404, detail="provider key not found")
|