talos-code 0.0.1 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +661 -0
- package/README.md +111 -3
- package/THIRD_PARTY_NOTICES.md +40 -0
- package/dist/archive/zip.js +73 -0
- package/dist/args.js +97 -0
- package/dist/automations/history-store.js +34 -0
- package/dist/automations/policy-store.js +43 -0
- package/dist/automations/runner.js +107 -0
- package/dist/automations/schedule.js +85 -0
- package/dist/automations/windows-task.js +53 -0
- package/dist/commands/advanced-cli.js +233 -0
- package/dist/commands/checkpoint-cli.js +45 -0
- package/dist/commands/config-cli.js +107 -0
- package/dist/commands/context.js +17 -0
- package/dist/commands/extensions-cli.js +207 -0
- package/dist/commands/project-cli.js +74 -0
- package/dist/commands/project-commands.js +143 -0
- package/dist/commands/provider-cli.js +185 -0
- package/dist/commands/services-cli.js +248 -0
- package/dist/commands/session-cli.js +171 -0
- package/dist/commands/system-cli.js +405 -0
- package/dist/config/commands.js +60 -0
- package/dist/config/load.js +382 -0
- package/dist/config/migrations.js +48 -0
- package/dist/config/types.js +11 -0
- package/dist/diagnostics/development-log.js +154 -0
- package/dist/diagnostics/doctor.js +104 -0
- package/dist/diagnostics/redact.js +80 -0
- package/dist/diagnostics/zip.js +52 -0
- package/dist/errors.js +156 -0
- package/dist/events/bridge.js +130 -0
- package/dist/extensions/installer.js +178 -0
- package/dist/extensions/package-schema.js +24 -0
- package/dist/headless/result.js +84 -0
- package/dist/headless/run.js +230 -0
- package/dist/i18n/en/approval.js +43 -0
- package/dist/i18n/en/common.js +9 -0
- package/dist/i18n/en/credentials.js +82 -0
- package/dist/i18n/en/errors.js +246 -0
- package/dist/i18n/en/firstrun.js +89 -0
- package/dist/i18n/en/providers.js +37 -0
- package/dist/i18n/en/screen.js +736 -0
- package/dist/i18n/en/tools.js +127 -0
- package/dist/i18n/en.js +14 -0
- package/dist/i18n/error-view.js +307 -0
- package/dist/i18n/index.js +19 -0
- package/dist/i18n/kernel-map.js +139 -0
- package/dist/io.js +45 -0
- package/dist/main.js +330 -0
- package/dist/paths.js +41 -0
- package/dist/protocol/v2/codec.js +9 -0
- package/dist/protocol/v2/events.js +555 -0
- package/dist/protocol/v2/index.js +4 -0
- package/dist/protocol/v2/replay.js +28 -0
- package/dist/protocol/v2/types.js +1 -0
- package/dist/provider/control-plane.js +135 -0
- package/dist/provider/environment-keys.js +275 -0
- package/dist/provider/health.js +110 -0
- package/dist/provider/missing-key.js +48 -0
- package/dist/provider/model-catalog.js +168 -0
- package/dist/provider/provider-text.js +13 -0
- package/dist/provider/store.js +95 -0
- package/dist/provider/system-keyring.js +214 -0
- package/dist/runtime/active-run.js +63 -0
- package/dist/runtime/agent-tree.js +74 -0
- package/dist/runtime/attachments.js +84 -0
- package/dist/runtime/brokered-executor.js +395 -0
- package/dist/runtime/context-status.js +76 -0
- package/dist/runtime/create-runtime.js +111 -0
- package/dist/runtime/model-profile.js +361 -0
- package/dist/runtime/output-store.js +137 -0
- package/dist/runtime/provider-attempts.js +185 -0
- package/dist/runtime/replay-buffer.js +153 -0
- package/dist/runtime/repo.js +95 -0
- package/dist/runtime/session-facade.js +135 -0
- package/dist/runtime/session-summary.js +125 -0
- package/dist/runtime/supervisor.js +262 -0
- package/dist/runtime/talos-composition.js +1006 -0
- package/dist/runtime/types.js +5 -0
- package/dist/runtime/usage-snapshot.js +82 -0
- package/dist/security/auto-classifier.js +38 -0
- package/dist/security/credential-free-environment.js +54 -0
- package/dist/security/evaluate.js +243 -0
- package/dist/security/execution-backends.js +236 -0
- package/dist/security/execution-broker.js +102 -0
- package/dist/security/forge-scan.js +74 -0
- package/dist/security/from-approval.js +39 -0
- package/dist/security/mxc-execution-backend.js +281 -0
- package/dist/security/permission-engine.js +138 -0
- package/dist/security/permission-explanation.js +54 -0
- package/dist/security/persist.js +179 -0
- package/dist/security/plugin-guard.js +230 -0
- package/dist/security/process-tree-evidence-store.js +74 -0
- package/dist/security/process-tree-probe.js +554 -0
- package/dist/security/project-resource-inventory.js +186 -0
- package/dist/security/project-trust-gate.js +38 -0
- package/dist/security/project-trust.js +367 -0
- package/dist/security/rule-parser.js +201 -0
- package/dist/security/shell-segmentation.js +68 -0
- package/dist/security/trust-authority.js +324 -0
- package/dist/security/types.js +1 -0
- package/dist/security/workspace-identity.js +102 -0
- package/dist/services/automation-facade.js +59 -0
- package/dist/services/forge-facade.js +77 -0
- package/dist/services/hook-facade.js +240 -0
- package/dist/services/index.js +16 -0
- package/dist/services/library-facade.js +174 -0
- package/dist/services/mcp-facade.js +348 -0
- package/dist/services/memory-facade.js +126 -0
- package/dist/services/notes-facade.js +157 -0
- package/dist/services/plugin-facade.js +308 -0
- package/dist/services/research-facade.js +110 -0
- package/dist/services/task-board-facade.js +236 -0
- package/dist/services/workflow-catalog.js +246 -0
- package/dist/sessions/export-format.js +98 -0
- package/dist/sessions/metadata-store.js +160 -0
- package/dist/sessions/share.js +122 -0
- package/dist/sessions/transfer.js +16 -0
- package/dist/subcommands.js +62 -0
- package/dist/tui/agent-roster.js +36 -0
- package/dist/tui/app.js +3761 -0
- package/dist/tui/approval.js +36 -0
- package/dist/tui/boot/boot-sequence.js +76 -0
- package/dist/tui/boot/cinematic.js +243 -0
- package/dist/tui/boot/desktop-logo.js +82 -0
- package/dist/tui/boot.js +3 -0
- package/dist/tui/busy-input.js +58 -0
- package/dist/tui/catalog-service.js +559 -0
- package/dist/tui/components/assistant-stream.js +1 -0
- package/dist/tui/components/command-menu.js +68 -0
- package/dist/tui/components/composer.js +219 -0
- package/dist/tui/components/diff.js +35 -0
- package/dist/tui/components/footer.js +48 -0
- package/dist/tui/components/header.js +6 -0
- package/dist/tui/components/markdown.js +305 -0
- package/dist/tui/components/status-indicator.js +32 -0
- package/dist/tui/components/terminal-shell.js +205 -0
- package/dist/tui/components/thinking-row.js +19 -0
- package/dist/tui/components/tool-row.js +97 -0
- package/dist/tui/components/transcript-virtualizer.js +165 -0
- package/dist/tui/components/transcript.js +43 -0
- package/dist/tui/diff-model.js +77 -0
- package/dist/tui/editor-history.js +21 -0
- package/dist/tui/editor.js +210 -0
- package/dist/tui/event-adapter.js +436 -0
- package/dist/tui/exit-output.js +58 -0
- package/dist/tui/external-editor.js +53 -0
- package/dist/tui/file-completion.js +89 -0
- package/dist/tui/focus-manager.js +5 -0
- package/dist/tui/highlight.js +30 -0
- package/dist/tui/input-router.js +18 -0
- package/dist/tui/interrupt.js +99 -0
- package/dist/tui/keybindings.js +194 -0
- package/dist/tui/keymap-resolver.js +91 -0
- package/dist/tui/launch-state.js +139 -0
- package/dist/tui/line-diff.js +57 -0
- package/dist/tui/live-activity.js +45 -0
- package/dist/tui/metrics.js +123 -0
- package/dist/tui/onboarding.js +36 -0
- package/dist/tui/overlays/agent-tree.js +39 -0
- package/dist/tui/overlays/approval-dialog.js +640 -0
- package/dist/tui/overlays/automation-center.js +51 -0
- package/dist/tui/overlays/checkpoint-picker.js +47 -0
- package/dist/tui/overlays/context-inspector.js +36 -0
- package/dist/tui/overlays/effort-line.js +110 -0
- package/dist/tui/overlays/forge-center.js +46 -0
- package/dist/tui/overlays/help-dialog.js +16 -0
- package/dist/tui/overlays/history-picker.js +37 -0
- package/dist/tui/overlays/hook-center.js +70 -0
- package/dist/tui/overlays/library-center.js +55 -0
- package/dist/tui/overlays/mcp-center.js +60 -0
- package/dist/tui/overlays/memory-center.js +63 -0
- package/dist/tui/overlays/model-picker.js +72 -0
- package/dist/tui/overlays/notes-center.js +53 -0
- package/dist/tui/overlays/overlay-host.js +8 -0
- package/dist/tui/overlays/plugin-center.js +76 -0
- package/dist/tui/overlays/provider-picker.js +145 -0
- package/dist/tui/overlays/queue-editor.js +32 -0
- package/dist/tui/overlays/research-center.js +59 -0
- package/dist/tui/overlays/scroll-window.js +234 -0
- package/dist/tui/overlays/session-picker.js +115 -0
- package/dist/tui/overlays/tasks-center.js +83 -0
- package/dist/tui/overlays/theme-picker.js +21 -0
- package/dist/tui/overlays/transcript-search.js +58 -0
- package/dist/tui/overlays/trust-center.js +29 -0
- package/dist/tui/overlays/workflow-center.js +46 -0
- package/dist/tui/project-file-index.js +214 -0
- package/dist/tui/project-references.js +85 -0
- package/dist/tui/project-trust-prompt.js +287 -0
- package/dist/tui/prompt-history-store.js +116 -0
- package/dist/tui/queue-store.js +110 -0
- package/dist/tui/regions/budget.js +59 -0
- package/dist/tui/regions/views.js +96 -0
- package/dist/tui/render-coordinator.js +148 -0
- package/dist/tui/render-scheduler.js +4 -0
- package/dist/tui/run.js +69 -0
- package/dist/tui/selection-list.js +29 -0
- package/dist/tui/session-controller.js +778 -0
- package/dist/tui/session-export.js +54 -0
- package/dist/tui/shell-input.js +35 -0
- package/dist/tui/shell-layout.js +48 -0
- package/dist/tui/shell-model.js +98 -0
- package/dist/tui/slash-commands.js +80 -0
- package/dist/tui/state.js +151 -0
- package/dist/tui/status-bar.js +125 -0
- package/dist/tui/status-view.js +40 -0
- package/dist/tui/terminal-capabilities.js +9 -0
- package/dist/tui/terminal-session.js +11 -0
- package/dist/tui/text-width.js +66 -0
- package/dist/tui/theme-catalog.js +25 -0
- package/dist/tui/theme-store.js +23 -0
- package/dist/tui/theme.js +23 -0
- package/dist/tui/tool-display.js +135 -0
- package/dist/tui/tool-facts.js +1 -0
- package/dist/tui/tools/bash-renderer.js +32 -0
- package/dist/tui/tools/change.js +61 -0
- package/dist/tui/tools/edit-renderer.js +17 -0
- package/dist/tui/tools/generic-renderer.js +13 -0
- package/dist/tui/tools/list-renderer.js +14 -0
- package/dist/tui/tools/read-renderer.js +13 -0
- package/dist/tui/tools/registry.js +20 -0
- package/dist/tui/tools/row-format.js +187 -0
- package/dist/tui/tools/search-renderer.js +38 -0
- package/dist/tui/tools/shared.js +97 -0
- package/dist/tui/tools/write-renderer.js +15 -0
- package/dist/tui/transcript-model.js +441 -0
- package/dist/tui/ui-preferences.js +79 -0
- package/dist/tui/usage-view.js +77 -0
- package/dist/tui/vim-mode.js +99 -0
- package/dist/update/check.js +15 -0
- package/dist/update/npm.js +51 -0
- package/dist/update/run.js +129 -0
- package/dist/version.js +2 -0
- package/dist/workspace/checkpoint-store.js +210 -0
- package/dist/workspace/checkpoint.js +413 -0
- package/dist/workspace/restore.js +232 -0
- package/package.json +63 -5
- package/vendor/context-engine/package.json +11 -0
- package/vendor/context-engine/src/compaction-planner.mjs +57 -0
- package/vendor/context-engine/src/contracts.mjs +48 -0
- package/vendor/context-engine/src/engine.mjs +445 -0
- package/vendor/context-engine/src/node/context-export.mjs +164 -0
- package/vendor/context-engine/src/node/legacy-import.mjs +92 -0
- package/vendor/context-engine/src/node/migrations/001-context.sql +126 -0
- package/vendor/context-engine/src/node/sqlite-store.mjs +85 -0
- package/vendor/context-engine/src/node/sqlite-worker.mjs +559 -0
- package/vendor/context-engine/src/profiles.mjs +10 -0
- package/vendor/context-engine/src/retrieval.mjs +104 -0
- package/vendor/context-engine/src/summary.mjs +97 -0
- package/vendor/context-engine/src/usage.mjs +34 -0
- package/vendor/harness-ui/package.json +38 -0
- package/vendor/harness-ui/src/acp-agent.mjs +350 -0
- package/vendor/harness-ui/src/agent-service.mjs +2188 -0
- package/vendor/harness-ui/src/agui-events.mjs +452 -0
- package/vendor/harness-ui/src/ambiente-solo-server.mjs +121 -0
- package/vendor/harness-ui/src/artifact-store.mjs +39 -0
- package/vendor/harness-ui/src/assistenza.mjs +123 -0
- package/vendor/harness-ui/src/automation-scheduler.mjs +69 -0
- package/vendor/harness-ui/src/automation-store.mjs +145 -0
- package/vendor/harness-ui/src/browser-annota.mjs +639 -0
- package/vendor/harness-ui/src/browser-frame.mjs +211 -0
- package/vendor/harness-ui/src/browser-proxy-universale.mjs +519 -0
- package/vendor/harness-ui/src/browser-proxy.mjs +87 -0
- package/vendor/harness-ui/src/browser-sessione-viva.mjs +329 -0
- package/vendor/harness-ui/src/browser-stream.mjs +445 -0
- package/vendor/harness-ui/src/browser-vivo.mjs +694 -0
- package/vendor/harness-ui/src/chat-image-attachments.mjs +72 -0
- package/vendor/harness-ui/src/config.mjs +697 -0
- package/vendor/harness-ui/src/contesto-del-progetto.mjs +385 -0
- package/vendor/harness-ui/src/context-asset-adapter.mjs +72 -0
- package/vendor/harness-ui/src/context-desktop-service.mjs +253 -0
- package/vendor/harness-ui/src/context-embedding-runtime.mjs +252 -0
- package/vendor/harness-ui/src/context-inference-scheduler.mjs +81 -0
- package/vendor/harness-ui/src/context-native-compaction.mjs +75 -0
- package/vendor/harness-ui/src/context-provider-adapter.mjs +141 -0
- package/vendor/harness-ui/src/context-runtime.mjs +118 -0
- package/vendor/harness-ui/src/context-token-counters.mjs +184 -0
- package/vendor/harness-ui/src/context-tool-catalog.mjs +86 -0
- package/vendor/harness-ui/src/context-tool-output.mjs +72 -0
- package/vendor/harness-ui/src/costo-elenco.mjs +252 -0
- package/vendor/harness-ui/src/custom-task.mjs +171 -0
- package/vendor/harness-ui/src/doctor.mjs +142 -0
- package/vendor/harness-ui/src/document-filename.mjs +97 -0
- package/vendor/harness-ui/src/document-generator.mjs +493 -0
- package/vendor/harness-ui/src/document-report.mjs +331 -0
- package/vendor/harness-ui/src/duckduckgo-search.mjs +155 -0
- package/vendor/harness-ui/src/elenco-profondo.mjs +337 -0
- package/vendor/harness-ui/src/favicon-proxy.mjs +113 -0
- package/vendor/harness-ui/src/forge-contract.mjs +221 -0
- package/vendor/harness-ui/src/frequent-dirs.mjs +134 -0
- package/vendor/harness-ui/src/generated-image-store.mjs +147 -0
- package/vendor/harness-ui/src/generation-idle.mjs +332 -0
- package/vendor/harness-ui/src/gguf-header.mjs +207 -0
- package/vendor/harness-ui/src/git-service.mjs +626 -0
- package/vendor/harness-ui/src/gitignore-elenco.mjs +604 -0
- package/vendor/harness-ui/src/harness-receipt-keypair.mjs +207 -0
- package/vendor/harness-ui/src/hf-direct-transfer.mjs +170 -0
- package/vendor/harness-ui/src/hf-hub-client.mjs +106 -0
- package/vendor/harness-ui/src/hf-image-proxy.mjs +105 -0
- package/vendor/harness-ui/src/hf-model-transfer.mjs +245 -0
- package/vendor/harness-ui/src/hook-registry.mjs +186 -0
- package/vendor/harness-ui/src/http-app.mjs +6259 -0
- package/vendor/harness-ui/src/http-lifecycle.mjs +132 -0
- package/vendor/harness-ui/src/id-archivio.mjs +27 -0
- package/vendor/harness-ui/src/image-generator.mjs +143 -0
- package/vendor/harness-ui/src/istruzioni-di-progetto.mjs +234 -0
- package/vendor/harness-ui/src/kernel/dist/kernelPerIlBanco.js +518 -0
- package/vendor/harness-ui/src/kernel/talosHarness.mjs +10437 -0
- package/vendor/harness-ui/src/library-policy-store.mjs +175 -0
- package/vendor/harness-ui/src/library-store.mjs +652 -0
- package/vendor/harness-ui/src/llama-server-supervisor.mjs +629 -0
- package/vendor/harness-ui/src/local-model-store.mjs +339 -0
- package/vendor/harness-ui/src/local-runtime-contract.mjs +66 -0
- package/vendor/harness-ui/src/local-runtime-events.mjs +44 -0
- package/vendor/harness-ui/src/local-runtime-llama-server.mjs +244 -0
- package/vendor/harness-ui/src/local-runtime-probe.mjs +401 -0
- package/vendor/harness-ui/src/machine-capacity.mjs +66 -0
- package/vendor/harness-ui/src/mappa-cartelle.mjs +491 -0
- package/vendor/harness-ui/src/mcp-client.mjs +98 -0
- package/vendor/harness-ui/src/mcp-registry.mjs +157 -0
- package/vendor/harness-ui/src/mcp-session.mjs +177 -0
- package/vendor/harness-ui/src/memory-store.mjs +227 -0
- package/vendor/harness-ui/src/model-catalog-models-dev.mjs +276 -0
- package/vendor/harness-ui/src/model-catalog.mjs +129 -0
- package/vendor/harness-ui/src/model-destination.mjs +189 -0
- package/vendor/harness-ui/src/modifica-ancorata.mjs +177 -0
- package/vendor/harness-ui/src/native-provider-adapter.mjs +205 -0
- package/vendor/harness-ui/src/notes-store.mjs +250 -0
- package/vendor/harness-ui/src/openai-compatible-runtime.mjs +428 -0
- package/vendor/harness-ui/src/openrouter-oauth.mjs +339 -0
- package/vendor/harness-ui/src/path-policy.mjs +442 -0
- package/vendor/harness-ui/src/plugin-registry.mjs +780 -0
- package/vendor/harness-ui/src/plugin-session.mjs +180 -0
- package/vendor/harness-ui/src/process-policy.mjs +345 -0
- package/vendor/harness-ui/src/prompt-enhancer-provider.mjs +94 -0
- package/vendor/harness-ui/src/provider-auth-cloud.mjs +95 -0
- package/vendor/harness-ui/src/provider-credential-store.mjs +541 -0
- package/vendor/harness-ui/src/provider-probe.mjs +582 -0
- package/vendor/harness-ui/src/provider-registry.mjs +1633 -0
- package/vendor/harness-ui/src/pty-terminal.mjs +312 -0
- package/vendor/harness-ui/src/public-problem.mjs +109 -0
- package/vendor/harness-ui/src/research/card.mjs +235 -0
- package/vendor/harness-ui/src/research/citations.mjs +142 -0
- package/vendor/harness-ui/src/research/collector.mjs +275 -0
- package/vendor/harness-ui/src/research/deposito-a-pezzi.mjs +139 -0
- package/vendor/harness-ui/src/research/dossier.mjs +114 -0
- package/vendor/harness-ui/src/research/esportazioni.mjs +560 -0
- package/vendor/harness-ui/src/research/fetch-cache.mjs +465 -0
- package/vendor/harness-ui/src/research/fidelity.mjs +122 -0
- package/vendor/harness-ui/src/research/independence.mjs +159 -0
- package/vendor/harness-ui/src/research/ledger.mjs +166 -0
- package/vendor/harness-ui/src/research/markdown-server.mjs +565 -0
- package/vendor/harness-ui/src/research/narration.mjs +181 -0
- package/vendor/harness-ui/src/research/open-cards.mjs +131 -0
- package/vendor/harness-ui/src/research/opposing.mjs +305 -0
- package/vendor/harness-ui/src/research/outline.mjs +111 -0
- package/vendor/harness-ui/src/research/page-budget.mjs +209 -0
- package/vendor/harness-ui/src/research/pdf.mjs +291 -0
- package/vendor/harness-ui/src/research/plan.mjs +301 -0
- package/vendor/harness-ui/src/research/raccolta-viva.mjs +452 -0
- package/vendor/harness-ui/src/research/recheck-document.mjs +69 -0
- package/vendor/harness-ui/src/research/recheck-history.mjs +192 -0
- package/vendor/harness-ui/src/research/recheck.mjs +194 -0
- package/vendor/harness-ui/src/research/report.mjs +203 -0
- package/vendor/harness-ui/src/research/run.mjs +527 -0
- package/vendor/harness-ui/src/research/synthesis.mjs +318 -0
- package/vendor/harness-ui/src/research/verification.mjs +572 -0
- package/vendor/harness-ui/src/research-orchestrator.mjs +2679 -0
- package/vendor/harness-ui/src/research-store.mjs +1133 -0
- package/vendor/harness-ui/src/runtime-build-manifest.mjs +26 -0
- package/vendor/harness-ui/src/runtime-contract.mjs +59 -0
- package/vendor/harness-ui/src/runtime-owner-adapter.mjs +1348 -0
- package/vendor/harness-ui/src/runtime-owner-contract.mjs +32 -0
- package/vendor/harness-ui/src/scheda-di-lavoro.mjs +249 -0
- package/vendor/harness-ui/src/search-source-store.mjs +172 -0
- package/vendor/harness-ui/src/session-registry.mjs +6095 -0
- package/vendor/harness-ui/src/session-store.mjs +220 -0
- package/vendor/harness-ui/src/sessione-pronta.mjs +73 -0
- package/vendor/harness-ui/src/setup-stato.mjs +31 -0
- package/vendor/harness-ui/src/sezioni-istruzioni.mjs +204 -0
- package/vendor/harness-ui/src/skill-registry.mjs +120 -0
- package/vendor/harness-ui/src/sse-replay-coalescente.mjs +0 -0
- package/vendor/harness-ui/src/static-files.mjs +96 -0
- package/vendor/harness-ui/src/stream-partition.mjs +123 -0
- package/vendor/harness-ui/src/subagent-orchestrator.mjs +453 -0
- package/vendor/harness-ui/src/task-catalog.mjs +65 -0
- package/vendor/harness-ui/src/tasks-store.mjs +220 -0
- package/vendor/harness-ui/src/terminal-registry.mjs +312 -0
- package/vendor/harness-ui/src/terminal-ws.mjs +170 -0
- package/vendor/harness-ui/src/tool-forge-store.mjs +299 -0
- package/vendor/harness-ui/src/tool-schema-normalize.mjs +100 -0
- package/vendor/harness-ui/src/usage-cache.mjs +315 -0
- package/vendor/harness-ui/src/workspace-browser.mjs +213 -0
- package/vendor/harness-ui/src/workspace-context.mjs +124 -0
- package/vendor/harness-ui/src/workspace-disk.mjs +62 -0
- package/vendor/harness-ui/src/workspace-files.mjs +589 -0
- package/vendor/harness-ui/src/workspace-info.mjs +189 -0
- package/vendor/harness-ui/src/workspace-launch-store.mjs +150 -0
- package/vendor/harness-ui/src/workspace-tree.mjs +67 -0
- package/vendor/harness-ui/src/workspace-watcher.mjs +161 -0
- package/vendor/manifest.json +170 -0
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* «Questa chiave funziona davvero?» — la domanda che il pannello Provider non
|
|
3
|
+
* sapeva porre.
|
|
4
|
+
*
|
|
5
|
+
* ## Perché esiste
|
|
6
|
+
*
|
|
7
|
+
* Owner, 03/9: la scheda Provider «è bruttissima», e guardandola è emerso un
|
|
8
|
+
* difetto più grave dell'estetica: si poteva salvare una chiave e non sapere
|
|
9
|
+
* MAI se era buona. Lo stato diceva «Chiave presente sul server» — cioè che
|
|
10
|
+
* una stringa era stata scritta da qualche parte, non che il provider la
|
|
11
|
+
* accetti. Sono due cose diverse, e la seconda è l'unica che interessa a chi
|
|
12
|
+
* sta per lanciare una sessione.
|
|
13
|
+
*
|
|
14
|
+
* ⭐ Ricerca 03/9 (docs.openwebui.com — «Connect a provider»; auth0 «check
|
|
15
|
+
* connection status»): il pattern affermato è una verifica esplicita che
|
|
16
|
+
* chiama davvero il provider e riporta l'esito con le sue parole — 200 =
|
|
17
|
+
* credenziale sufficiente, 401 = non autorizzata. Non un controllo di forma
|
|
18
|
+
* sulla stringa.
|
|
19
|
+
*
|
|
20
|
+
* ## Cosa fa, e cosa NON fa
|
|
21
|
+
*
|
|
22
|
+
* La sonda ordinaria chiede l'ELENCO DEI MODELLI, quando documentato, senza
|
|
23
|
+
* generare token. P-J: quando manca, lo dichiara; la sonda minima di generazione
|
|
24
|
+
* si può eseguire solo passando consentiGenerazione: true. Può consumare quota
|
|
25
|
+
* o credito e non viene attivata dalla rotta HTTP ordinaria.
|
|
26
|
+
*
|
|
27
|
+
* ⛔ Non prova che una GENERAZIONE riuscirà: una chiave valida può avere
|
|
28
|
+
* credito esaurito o un modello negato. Prova che il provider ci riconosce, e
|
|
29
|
+
* la UI non dice più di questo.
|
|
30
|
+
*
|
|
31
|
+
* ⛔ La chiave non esce mai da qui verso il browser: entra dal portachiavi,
|
|
32
|
+
* viaggia nell'intestazione, e di ritorno va solo l'esito.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
import { ID_CON_CREDENZIALE, REGISTRO_FORNITORI, catalogoDiRiservaPer } from './provider-registry.mjs';
|
|
36
|
+
// P-K
|
|
37
|
+
import { destinazioneCloud } from './provider-auth-cloud.mjs';
|
|
38
|
+
import { leggiRuntimeAgenteEsterno, connettiAgenteAcp } from './acp-agent.mjs'; // P-L-bis
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Come si chiede l'elenco dei modelli a ciascuno.
|
|
42
|
+
*
|
|
43
|
+
* ⛔ Una tabella e non un `if` per provider: quando se ne aggiunge uno, il
|
|
44
|
+
* posto dove scriverlo è uno solo e si vede subito cosa manca. E ogni riga
|
|
45
|
+
* dice come si autentica QUEL provider — sono tre schemi diversi (Bearer,
|
|
46
|
+
* `x-api-key`, chiave in query), e confonderli è il modo tipico di ottenere
|
|
47
|
+
* un 401 che sembra «chiave sbagliata» quando è «intestazione sbagliata».
|
|
48
|
+
*
|
|
49
|
+
* ⛔⛔ 12/09 — P-A: la tabella era GIÀ la forma giusta, e per questo non si riscrive: le si toglie
|
|
50
|
+
* il suo elenco e le si dà quello del registro (§6c dell'inventario). Il commento qui sopra
|
|
51
|
+
* prometteva «il posto dove scriverlo è uno solo» — era vero dentro questo file, e falso nel
|
|
52
|
+
* repo: gli stessi sette nomi stavano in altri dodici posti. Adesso la promessa è mantenuta.
|
|
53
|
+
*/
|
|
54
|
+
export const SONDE_PROVIDER = Object.freeze(Object.fromEntries(ID_CON_CREDENZIALE.map((id) => {
|
|
55
|
+
const sonda = REGISTRO_FORNITORI[id].sonda;
|
|
56
|
+
return [id, Object.freeze({
|
|
57
|
+
...(sonda.urlAssoluto ? { urlAssoluto: sonda.urlAssoluto } : { percorso: sonda.percorso }),
|
|
58
|
+
auth: sonda.auth,
|
|
59
|
+
attiva: sonda.attiva !== false,
|
|
60
|
+
conta: sonda.conta,
|
|
61
|
+
...(sonda.dallaRadice ? { dallaRadice: true } : {}),
|
|
62
|
+
...(sonda.catalogoPubblico ? { catalogoPubblico: true } : {}),
|
|
63
|
+
// P-J — descrizione della sonda minima, mai un'autorizzazione implicita a generare.
|
|
64
|
+
...(sonda.richiestaMinima ? { richiestaMinima: sonda.richiestaMinima } : {}),
|
|
65
|
+
// CLI-REQ-06 — il wire viaggia con la sonda: è ciò che dice COME si legge la risposta minima,
|
|
66
|
+
// e una tabella iniettata nei test deve poterlo dichiarare senza passare dal registro.
|
|
67
|
+
wire: REGISTRO_FORNITORI[id].wire,
|
|
68
|
+
})];
|
|
69
|
+
})));
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gli esiti che la UI sa disegnare. Nessun altro valore esce da qui.
|
|
73
|
+
*
|
|
74
|
+
* ⛔ `non-sondabile` è il sesto, aggiunto il 12/09 col registro: è il fornitore il cui elenco
|
|
75
|
+
* modelli risponde 401 **anche con una chiave buona** (in Hermes è il caso Xiaomi MiMo,
|
|
76
|
+
* `supports_health_check=False`). Senza quello stato la sonda **accuserebbe una chiave valida**
|
|
77
|
+
* — cioè esattamente il difetto contro cui questa sonda è nata. Oggi nessuno dei nostri lo
|
|
78
|
+
* dichiara: la riga esiste perché il primo che lo farà non debba inventarsi uno stato.
|
|
79
|
+
*/
|
|
80
|
+
export const ESITI_SONDA = Object.freeze(['collegato', 'non-autorizzato', 'irraggiungibile', 'non-provabile', 'non-sondabile', 'errore']);
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* I fornitori il cui catalogo si chiede **a loro**, e che hanno una scheda propria nel selettore.
|
|
84
|
+
*
|
|
85
|
+
* ⛔ Era un array letterale dentro la funzione — `['openai','anthropic','gemini']`, il terzo dei
|
|
86
|
+
* tredici elenchi. Adesso è il registro a dirlo: `catalogo.inUI` (ha una scheda) più
|
|
87
|
+
* `catalogo.fonte === 'fornitore'` (il catalogo arriva dalla sua API, non da un motore locale —
|
|
88
|
+
* LM Studio ha la scheda ma il suo elenco lo dà il runtime che lo carica e lo scarica già).
|
|
89
|
+
*/
|
|
90
|
+
export const CATALOGHI_DIRETTI = Object.freeze(
|
|
91
|
+
Object.values(REGISTRO_FORNITORI).filter((r) => r.catalogo?.inUI === true && r.catalogo?.fonte === 'fornitore').map((r) => r.id),
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
export class ProviderProbeError extends Error {
|
|
95
|
+
constructor(message, code = 'PROVIDER_PROBE_FAILED') {
|
|
96
|
+
super(message);
|
|
97
|
+
this.name = 'ProviderProbeError';
|
|
98
|
+
this.code = code;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function urlDellaSonda(sonda, endpoint) {
|
|
103
|
+
if (sonda.urlAssoluto) return sonda.urlAssoluto;
|
|
104
|
+
if (typeof endpoint !== 'string' || endpoint.trim() === '') {
|
|
105
|
+
throw new ProviderProbeError('provider endpoint is missing', 'PROVIDER_RUNTIME_INVALID');
|
|
106
|
+
}
|
|
107
|
+
if (sonda.dallaRadice) return new URL(sonda.percorso, endpoint).toString();
|
|
108
|
+
return `${endpoint.replace(/\/+$/u, '')}${sonda.percorso}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* CLI-REQ-06 — quanti byte si accettano dalla risposta alla richiesta minima.
|
|
113
|
+
*
|
|
114
|
+
* ⛔ Perché un tetto esiste. L'indirizzo del fornitore lo imposta la persona (`runtime.endpoint`) e
|
|
115
|
+
* il corpo lo decide chi risponde: senza limite, `risposta.json()` legge tutto quello che arriva
|
|
116
|
+
* PRIMA che qualcuno possa giudicarlo. Misurato il 17/09/2026 su questa stessa funzione: 400 MiB
|
|
117
|
+
* letti per intero, 1710 MiB di memoria di picco — e poi la risposta veniva pure ACCETTATA.
|
|
118
|
+
*
|
|
119
|
+
* ⛔ Perché SESSANTAQUATTRO KiB. La risposta a una generazione da UN token sta in poche centinaia
|
|
120
|
+
* di byte: il tetto è ~100 volte quello, così ci stanno impronte, metadati e un `error` lungo
|
|
121
|
+
* senza che un fornitore onesto venga mai tagliato. È lo stesso ordine di grandezza dei tetti già
|
|
122
|
+
* in uso nel repo per i corpi di controllo (`leggiCorpoJson(req, 16 * 1024)` in `http-app.mjs`),
|
|
123
|
+
* quattro volte più largo perché qui il corpo lo scrive un estraneo e non la nostra UI.
|
|
124
|
+
*
|
|
125
|
+
* ⛔ Non si applica all'elenco modelli: quello può avere migliaia di righe legittime.
|
|
126
|
+
*/
|
|
127
|
+
const TETTO_RISPOSTA_MINIMA = 64 * 1024;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Legge il corpo a flusso e si FERMA al tetto, senza mai accumularlo tutto.
|
|
131
|
+
*
|
|
132
|
+
* @returns {Promise<{testo?: string, troppoGrande?: boolean, interrotta?: unknown}>}
|
|
133
|
+
* `troppoGrande` quando si supera il tetto (la lettura viene annullata sul posto),
|
|
134
|
+
* `interrotta` con l'errore quando la lettura si spezza o scade a metà — che NON è
|
|
135
|
+
* «il fornitore ha risposto male»: è «non abbiamo la risposta».
|
|
136
|
+
*/
|
|
137
|
+
async function corpoEntroIlTetto(risposta, tetto) {
|
|
138
|
+
if (!risposta.body) {
|
|
139
|
+
try {
|
|
140
|
+
const testo = await risposta.text();
|
|
141
|
+
return Buffer.byteLength(testo) > tetto ? { troppoGrande: true } : { testo };
|
|
142
|
+
} catch (errore) { return { interrotta: errore }; }
|
|
143
|
+
}
|
|
144
|
+
const lettore = risposta.body.getReader();
|
|
145
|
+
const pezzi = [];
|
|
146
|
+
let byte = 0;
|
|
147
|
+
try {
|
|
148
|
+
for (;;) {
|
|
149
|
+
const { done, value } = await lettore.read();
|
|
150
|
+
if (done) break;
|
|
151
|
+
byte += value.byteLength;
|
|
152
|
+
if (byte > tetto) {
|
|
153
|
+
await lettore.cancel().catch(() => {});
|
|
154
|
+
return { troppoGrande: true };
|
|
155
|
+
}
|
|
156
|
+
pezzi.push(value);
|
|
157
|
+
}
|
|
158
|
+
} catch (errore) {
|
|
159
|
+
await lettore.cancel().catch(() => {});
|
|
160
|
+
return { interrotta: errore };
|
|
161
|
+
}
|
|
162
|
+
return { testo: Buffer.concat(pezzi).toString('utf8') };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* CLI-REQ-06 — dà a un guasto di rete il SUO nome.
|
|
167
|
+
*
|
|
168
|
+
* ⛔ Prima c'erano due frasi per tre cause, e la terza finiva nella più generica: un fornitore che
|
|
169
|
+
* risponde `302` verso un altro indirizzo (che non seguiamo, `redirect: 'error'`) veniva
|
|
170
|
+
* annunciato come «non è stato possibile raggiungere», cioè come se fosse spento. Ha risposto,
|
|
171
|
+
* eccome: ci ha mandati altrove, e chi legge deve poter guardare l'indirizzo che ha impostato.
|
|
172
|
+
*/
|
|
173
|
+
function motivoDiRete(errore, etichetta, secondi) {
|
|
174
|
+
if (errore?.name === 'TimeoutError' || errore?.name === 'AbortError') return `${etichetta}: nessuna risposta entro ${secondi} secondi.`;
|
|
175
|
+
const dettaglio = `${errore?.message ?? ''} ${errore?.cause?.message ?? ''}`;
|
|
176
|
+
if (/redirect/iu.test(dettaglio)) return `${etichetta}: la risposta rinvia a un altro indirizzo, e non lo seguiamo. Controlla l'indirizzo impostato per questo fornitore.`;
|
|
177
|
+
return `Non è stato possibile raggiungere ${etichetta}.`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** P-I: catalogo nativo DashScope v1; HTTP 200 non basta se il corpo dichiara un errore. */
|
|
181
|
+
function paginaDashScope(corpo, numero = 1) {
|
|
182
|
+
const p = corpo?.output;
|
|
183
|
+
if (corpo?.success !== true || !p || !Number.isSafeInteger(p.total) || p.total < 0
|
|
184
|
+
|| p.page_no !== numero || !Number.isSafeInteger(p.page_size) || p.page_size < 1
|
|
185
|
+
|| !Array.isArray(p.models) || p.models.length > p.page_size || p.models.length > p.total
|
|
186
|
+
|| (p.total > 0 && !p.models.length)
|
|
187
|
+
|| p.models.some(m => !m || typeof m.model !== 'string' || !m.model.trim())
|
|
188
|
+
|| new Set(p.models.map(m => m.model)).size !== p.models.length) {
|
|
189
|
+
throw new ProviderProbeError('Qwen: pagina del catalogo non valida o incompleta.', 'CATALOG_UPSTREAM_ERROR');
|
|
190
|
+
}
|
|
191
|
+
return p;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
/**
|
|
195
|
+
* @param {object} deps
|
|
196
|
+
* @param {(provider: string) => string|null} deps.leggiChiave dal portachiavi, mai dal browser
|
|
197
|
+
* @param {(provider: string) => {endpoint: string|null, timeoutSeconds: number}} deps.leggiRuntime
|
|
198
|
+
* @param {typeof fetch} [deps.fetchImpl]
|
|
199
|
+
* @param {() => number} [deps.orologio] millisecondi monotoni, per la latenza
|
|
200
|
+
* @param {Record<string,object>} [deps.sonde] la tabella delle sonde. ⛔ Iniettabile per UNA
|
|
201
|
+
* ragione sola: lo stato `non-sondabile` deve poter essere provato PRIMA che esista un
|
|
202
|
+
* fornitore che lo dichiara — una guardia che nessuno sa far scattare è una guardia che nessuno
|
|
203
|
+
* sa se funziona. In produzione resta sempre quella del registro.
|
|
204
|
+
*/
|
|
205
|
+
export function createProviderProbe({ leggiChiave, leggiRuntime, fetchImpl = fetch, orologio = () => performance.now(), sonde = SONDE_PROVIDER, env = process.env } = {}) {
|
|
206
|
+
if (typeof leggiChiave !== 'function' || typeof leggiRuntime !== 'function') {
|
|
207
|
+
throw new ProviderProbeError('probe dependencies are invalid', 'PROVIDER_PROBE_MISCONFIGURED');
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
let identitaAgente = null;
|
|
211
|
+
function runtimeAgente() {
|
|
212
|
+
try { return leggiRuntimeAgenteEsterno(leggiRuntime('esterno'), { env }); }
|
|
213
|
+
catch (e) {
|
|
214
|
+
if (e.code === 'ACP_NOT_CONFIGURED') throw new ProviderProbeError("Configura l'agente esterno in Fornitori e accessi", 'CATALOG_CONFIGURATION_REQUIRED');
|
|
215
|
+
throw e;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
const modelliConfigurati = (provider, runtime) => (runtime?.modelli ?? []).map(m => ({
|
|
219
|
+
id: `${provider}:${m.id}`, modelId: m.id, nome: m.nome || m.id, provider,
|
|
220
|
+
fonte: 'configurazione', credenzialeVerificata: false, toolCalling: 'ignoto',
|
|
221
|
+
capacita: { toolCall: null, reasoning: null }, contextLength: null,
|
|
222
|
+
}));
|
|
223
|
+
|
|
224
|
+
/*
|
|
225
|
+
* CLI-REQ-06 — il CONTRATTO di `credenzialeVerificata`, per chi legge da fuori (oggi la CLI).
|
|
226
|
+
*
|
|
227
|
+
* true → il fornitore ha accettato una richiesta autenticata fatta con QUESTA chiave;
|
|
228
|
+
* false → l'ha guardata e non la conferma. `esito` dice come: `non-autorizzato` = rifiutata,
|
|
229
|
+
* `collegato` = raggiunto un catalogo PUBBLICO, che di quella chiave non dice nulla;
|
|
230
|
+
* null → nessuno l'ha giudicata (nessuna chiave, fornitore irraggiungibile, traffico, 404,
|
|
231
|
+
* risposta illeggibile). ⛔ Mai `undefined`: chi legge JSON non distingue «campo
|
|
232
|
+
* assente» da «non lo so», e sarebbe costretto a indovinare proprio dove sbagliare
|
|
233
|
+
* costa il salvataggio di una chiave rifiutata.
|
|
234
|
+
*/
|
|
235
|
+
const nonGiudicata = (campi) => ({ credenzialeVerificata: null, ...campi });
|
|
236
|
+
|
|
237
|
+
async function prova(provider, { consentiGenerazione = false } = {}) { // P-J: opzione solo esplicita.
|
|
238
|
+
// P-L-bis: il catalogo non avvia il processo; solo questa azione negozia e chiude.
|
|
239
|
+
if (provider === 'esterno') {
|
|
240
|
+
let runtime;
|
|
241
|
+
try { runtime = runtimeAgente(); }
|
|
242
|
+
catch (e) { return { provider, esito: 'non-provabile', motivo: e.message, codice: e.code, modelli: null, millisecondi: null }; }
|
|
243
|
+
const partito = orologio();
|
|
244
|
+
identitaAgente = null;
|
|
245
|
+
try {
|
|
246
|
+
const agente = await connettiAgenteAcp(runtime, { env, soloInizializzazione: true });
|
|
247
|
+
await agente.chiudi();
|
|
248
|
+
identitaAgente = { runtime: JSON.stringify(runtime), nome: agente.nome };
|
|
249
|
+
return { provider, esito: 'collegato', motivo: 'Agente inizializzato e chiuso. Nessun messaggio inviato.',
|
|
250
|
+
modelli: 1, millisecondi: Math.round(orologio() - partito), credenzialeVerificata: false };
|
|
251
|
+
} catch (e) { return { provider, esito: 'errore', motivo: e.message, codice: e.code, modelli: null, millisecondi: Math.round(orologio() - partito) }; }
|
|
252
|
+
}
|
|
253
|
+
const sonda = sonde[provider];
|
|
254
|
+
if (!sonda) throw new ProviderProbeError(`unknown provider ${provider}`, 'PROVIDER_INVALID');
|
|
255
|
+
const record = REGISTRO_FORNITORI[provider];
|
|
256
|
+
const etichetta = record?.etichetta ?? 'Il fornitore';
|
|
257
|
+
|
|
258
|
+
/*
|
|
259
|
+
* ⛔ Chi dichiara di non essere sondabile NON viene chiamato: zero richieste, e lo si dice.
|
|
260
|
+
* Una sonda che parte comunque su un `/models` che risponde 401 per progetto restituirebbe
|
|
261
|
+
* «credenziale rifiutata» su una chiave buona.
|
|
262
|
+
*/
|
|
263
|
+
if (sonda.attiva === false && !sonda.richiestaMinima) {
|
|
264
|
+
return nonGiudicata({ provider, esito: 'non-sondabile', motivo: 'Questo fornitore non espone un elenco modelli su cui provare la credenziale.', modelli: null, millisecondi: null });
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
const chiave = leggiChiave(provider);
|
|
268
|
+
/*
|
|
269
|
+
* ⛔ Senza chiave NON si chiama e NON si dice «non autorizzato»: non c'è
|
|
270
|
+
* niente da autorizzare. «non-provabile» è un terzo stato, e serve —
|
|
271
|
+
* confonderlo con un rifiuto manderebbe la persona a cercare una chiave
|
|
272
|
+
* sbagliata invece di inserirne una.
|
|
273
|
+
*/
|
|
274
|
+
if (sonda.auth !== 'nessuna' && sonda.auth !== 'bearer-facoltativo' && !chiave) {
|
|
275
|
+
return nonGiudicata({ provider, esito: 'non-provabile', motivo: `Nessuna chiave salvata per ${etichetta}.`, modelli: null, millisecondi: null });
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/*
|
|
279
|
+
* P-J — l'azione ordinaria resta senza generazione. Solo il chiamante che dichiara il consenso
|
|
280
|
+
* può eseguire il POST minimo della sonda, separato dalle chat del kernel.
|
|
281
|
+
*
|
|
282
|
+
* ⛔ CLI-REQ-06, 17/09 — la prova da un token AFFIANCA l'elenco modelli, non lo sostituisce.
|
|
283
|
+
* Chi ha entrambi (i quattro a catalogo pubblico) senza consenso continua a chiedere il
|
|
284
|
+
* catalogo, come ha sempre fatto; solo chi non ha un elenco su cui provare (`attiva: false`,
|
|
285
|
+
* oggi `zai-anthropic`) si dichiara non sondabile. Legare il rifiuto alla sola presenza della
|
|
286
|
+
* richiesta minima avrebbe SPENTO la sonda ordinaria di quattro fornitori per aggiungerne una.
|
|
287
|
+
*/
|
|
288
|
+
const generabile = sonda.richiestaMinima;
|
|
289
|
+
const minima = generabile && consentiGenerazione === true ? generabile : null;
|
|
290
|
+
if (generabile && !minima && sonda.attiva === false) {
|
|
291
|
+
return nonGiudicata({ provider, esito: 'non-sondabile', motivo: `${etichetta}: non è documentato un elenco modelli per verificare la chiave. La prova minima richiede una generazione con limite di un token e può consumare credito o quota; occorre richiederla esplicitamente.`, modelli: null, millisecondi: null });
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const runtime = leggiRuntime(provider) || {};
|
|
295
|
+
let url;
|
|
296
|
+
// P-K — la costruzione è condivisa con il trasporto, compresa l'eventuale scadenza.
|
|
297
|
+
let cloud;
|
|
298
|
+
if (record?.cloud) {
|
|
299
|
+
try { cloud = destinazioneCloud(provider, runtime, chiave, null, { catalogo: true }); }
|
|
300
|
+
catch (errore) { return { provider, esito: 'non-provabile', motivo: errore.message, codice: errore.code, modelli: null, millisecondi: null }; }
|
|
301
|
+
}
|
|
302
|
+
// P-K — fine
|
|
303
|
+
try {
|
|
304
|
+
url = cloud?.url ?? urlDellaSonda(minima ?? sonda, runtime.endpoint); // P-J (richiesta minima) + P-K (indirizzo cloud)
|
|
305
|
+
} catch (errore) {
|
|
306
|
+
return nonGiudicata({ provider, esito: 'non-provabile', motivo: `Manca l'indirizzo di ${etichetta}.`, modelli: null, millisecondi: null, codice: errore.code });
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const intestazioni = { Accept: 'application/json' };
|
|
310
|
+
if (sonda.auth === 'bearer' || (sonda.auth === 'bearer-facoltativo' && chiave)) intestazioni.Authorization = `Bearer ${chiave}`;
|
|
311
|
+
if (sonda.auth === 'x-api-key') { intestazioni['x-api-key'] = chiave; intestazioni['anthropic-version'] = '2023-06-01'; }
|
|
312
|
+
// P-J — versione anche per la porta Anthropic autenticata con Bearer.
|
|
313
|
+
if (record?.wire === 'anthropic-messages') intestazioni['anthropic-version'] = '2023-06-01';
|
|
314
|
+
if (minima) intestazioni['Content-Type'] = 'application/json';
|
|
315
|
+
if (sonda.auth === 'query') url += `${url.includes('?') ? '&' : '?'}key=${encodeURIComponent(chiave)}`;
|
|
316
|
+
// P-K — una chiave Azure e un token Entra usano intestazioni diverse.
|
|
317
|
+
if (cloud) { for (const key of Object.keys(intestazioni)) delete intestazioni[key]; Object.assign(intestazioni, { Accept: 'application/json' }, cloud.headers); }
|
|
318
|
+
// P-K — fine
|
|
319
|
+
|
|
320
|
+
const secondi = Number.isFinite(runtime.timeoutSeconds) && runtime.timeoutSeconds > 0 ? runtime.timeoutSeconds : 60;
|
|
321
|
+
// ⛔ Il tempo massimo è quello che la persona ha impostato per questo
|
|
322
|
+
// provider, non una costante nostra: un pannello che ignora il proprio
|
|
323
|
+
// campo insegna che quel campo non serve.
|
|
324
|
+
const stop = AbortSignal.timeout(Math.min(secondi, 30) * 1_000);
|
|
325
|
+
const partito = orologio();
|
|
326
|
+
let risposta;
|
|
327
|
+
try {
|
|
328
|
+
risposta = await fetchImpl(url, { method: minima ? 'POST' : 'GET', ...(minima ? { body: JSON.stringify(minima.corpo) } : {}), headers: intestazioni, signal: stop, redirect: 'error' }); // P-J
|
|
329
|
+
} catch (errore) {
|
|
330
|
+
return nonGiudicata({
|
|
331
|
+
provider,
|
|
332
|
+
esito: 'irraggiungibile',
|
|
333
|
+
motivo: motivoDiRete(errore, etichetta, Math.min(secondi, 30)),
|
|
334
|
+
modelli: null,
|
|
335
|
+
millisecondi: Math.round(orologio() - partito),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const millisecondi = Math.round(orologio() - partito);
|
|
339
|
+
if (risposta.status === 401 || risposta.status === 403) {
|
|
340
|
+
// P-K — un 403 non dimostra una chiave errata; può essere il permesso del progetto/modello.
|
|
341
|
+
// CLI-REQ-06 — un rifiuto è un GIUDIZIO sulla chiave: `false`, mai `null`.
|
|
342
|
+
if (record?.cloud) return { provider, esito: 'non-autorizzato', credenzialeVerificata: false, motivo: risposta.status === 401
|
|
343
|
+
? `${etichetta}: credenziale non accettata (HTTP 401).`
|
|
344
|
+
: `${etichetta}: accesso negato (HTTP 403); controlla i permessi.`, modelli: null, millisecondi, httpStatus: risposta.status };
|
|
345
|
+
// P-K — fine
|
|
346
|
+
return { provider, esito: 'non-autorizzato', credenzialeVerificata: false, motivo: `${etichetta} ha rifiutato la credenziale (HTTP ${risposta.status}).`, modelli: null, millisecondi, httpStatus: risposta.status };
|
|
347
|
+
}
|
|
348
|
+
if (!risposta.ok) {
|
|
349
|
+
/*
|
|
350
|
+
* ⛔ CLI-REQ-06 — un 404 sulla PROVA DA UN TOKEN non ha la stessa causa di un 404 sull'elenco.
|
|
351
|
+
* L'indirizzo della chat lo usa tutto il prodotto: se fosse sbagliato non funzionerebbe
|
|
352
|
+
* niente. Su questa strada la causa di gran lunga più probabile è che il modello scelto per
|
|
353
|
+
* la prova non sia più servito — e dirlo indirizza chi legge verso la cosa da cambiare,
|
|
354
|
+
* invece di mandarlo a caccia di un indirizzo che va benissimo.
|
|
355
|
+
*/
|
|
356
|
+
const motivo = risposta.status === 404
|
|
357
|
+
? minima
|
|
358
|
+
? `${etichetta}: la prova non ha trovato il modello che usa per controllare la chiave; potrebbe non essere più disponibile. La chiave non è verificata da questa risposta.`
|
|
359
|
+
: `${etichetta}: elenco modelli non trovato (HTTP 404). La validità della chiave non è verificata da questa risposta.`
|
|
360
|
+
: `${etichetta} ha risposto HTTP ${risposta.status}.`;
|
|
361
|
+
return nonGiudicata({ provider, esito: 'errore', motivo, modelli: null, millisecondi, httpStatus: risposta.status });
|
|
362
|
+
}
|
|
363
|
+
let corpo = null;
|
|
364
|
+
let letturaInterrotta = null;
|
|
365
|
+
let rispostaTroppoGrande = false;
|
|
366
|
+
if (minima) {
|
|
367
|
+
// ⛔ Il corpo di una generazione da un token si legge a flusso e con un tetto: vedi
|
|
368
|
+
// `TETTO_RISPOSTA_MINIMA`. L'elenco modelli no — quello può essere legittimamente grande.
|
|
369
|
+
const letto = await corpoEntroIlTetto(risposta, TETTO_RISPOSTA_MINIMA);
|
|
370
|
+
if (letto.troppoGrande) rispostaTroppoGrande = true;
|
|
371
|
+
else if (letto.interrotta) letturaInterrotta = letto.interrotta;
|
|
372
|
+
else { try { corpo = JSON.parse(letto.testo); } catch { corpo = null; } }
|
|
373
|
+
} else {
|
|
374
|
+
try { corpo = await risposta.json(); } catch { corpo = null; }
|
|
375
|
+
}
|
|
376
|
+
/*
|
|
377
|
+
* ⛔ CLI-REQ-06 — una lettura che si SPEZZA a metà non è «il fornitore ha risposto male»: è
|
|
378
|
+
* «non abbiamo la risposta». Prima finiva nel ramo del corpo malformato e usciva come
|
|
379
|
+
* «HTTP 200 senza un messaggio valido», cioè accusava il fornitore di un guasto nostro o
|
|
380
|
+
* della rete. Il tempo massimo scade proprio qui quando il corpo non finisce mai.
|
|
381
|
+
*/
|
|
382
|
+
if (letturaInterrotta) {
|
|
383
|
+
return nonGiudicata({ provider, esito: 'irraggiungibile', motivo: motivoDiRete(letturaInterrotta, etichetta, Math.min(secondi, 30)),
|
|
384
|
+
modelli: null, millisecondi: Math.round(orologio() - partito), httpStatus: risposta.status });
|
|
385
|
+
}
|
|
386
|
+
/*
|
|
387
|
+
* P-J — 200 senza una risposta riconoscibile non verifica la chiave.
|
|
388
|
+
*
|
|
389
|
+
* ⛔ CLI-REQ-06 — «riconoscibile» dipende dal WIRE del fornitore, non dalla forma che la sonda
|
|
390
|
+
* ha imparato per prima: fino al 17/09 questo blocco accettava solo un corpo Anthropic
|
|
391
|
+
* Messages, quindi un 200 perfettamente valido in forma OpenAI sarebbe stato letto come
|
|
392
|
+
* «credenziale non verificata». Un wire che non sappiamo leggere NON diventa un successo.
|
|
393
|
+
*/
|
|
394
|
+
if (minima) {
|
|
395
|
+
if (rispostaTroppoGrande) {
|
|
396
|
+
return nonGiudicata({ provider, esito: 'errore',
|
|
397
|
+
motivo: `${etichetta}: la risposta è troppo grande per essere una generazione da un token, e non è stata letta oltre il limite. La chiave non è stata giudicata.`,
|
|
398
|
+
modelli: null, millisecondi, httpStatus: risposta.status });
|
|
399
|
+
}
|
|
400
|
+
const wire = sonda.wire ?? record?.wire;
|
|
401
|
+
/*
|
|
402
|
+
* ⛔ UN 200 CHE DICHIARA UN ERRORE NON È UN SÌ. Misurato il 17/09/2026 sulla prima stesura:
|
|
403
|
+
* `{choices:[…], usage:{…}, error:{message:'invalid api key'}}` usciva come `collegato` +
|
|
404
|
+
* `credenzialeVerificata: true` — cioè il prodotto avrebbe SALVATO una chiave rifiutata.
|
|
405
|
+
* La regola esisteva già in questo file, su un altro catalogo (`paginaDashScope`: «HTTP 200
|
|
406
|
+
* non basta se il corpo dichiara un errore»); semplicemente non era stata applicata qui.
|
|
407
|
+
* Vale per ENTRAMBI i wire: un intermediario può impacchettare un rifiuto dentro un 200.
|
|
408
|
+
* `error: null` non è una dichiarazione di errore — è come molti fornitori dicono «tutto ok».
|
|
409
|
+
*/
|
|
410
|
+
const dichiaraErrore = corpo != null && typeof corpo === 'object' && corpo.error != null;
|
|
411
|
+
/*
|
|
412
|
+
* ⛔ `usage` si controlla SE c'è, e non si pretende. Il handoff della CLI chiedeva «choices
|
|
413
|
+
* non vuoto E `usage.prompt_tokens` finito»: pretenderlo sarebbe un falso NEGATIVO su una
|
|
414
|
+
* chiave buona, perché due dei quattro fornitori (Novita e Ollama Cloud) non documentano il
|
|
415
|
+
* corpo della risposta e non possiamo promettere che quel campo arrivi. Se però arriva e non
|
|
416
|
+
* è un numero, la risposta non è quella che dice di essere.
|
|
417
|
+
*/
|
|
418
|
+
const usoCoerente = (valore) => valore === undefined || (typeof valore === 'number' && Number.isFinite(valore));
|
|
419
|
+
const valida = !dichiaraErrore && (wire === 'anthropic-messages'
|
|
420
|
+
? corpo?.type === 'message' && corpo.role === 'assistant' && typeof corpo.id === 'string'
|
|
421
|
+
&& Array.isArray(corpo.content) && typeof corpo.usage?.input_tokens === 'number' && Number.isFinite(corpo.usage.input_tokens)
|
|
422
|
+
: wire === 'openai-chat'
|
|
423
|
+
? Array.isArray(corpo?.choices) && corpo.choices.length > 0 && usoCoerente(corpo.usage?.prompt_tokens)
|
|
424
|
+
: false);
|
|
425
|
+
return { provider, esito: valida ? 'collegato' : 'errore',
|
|
426
|
+
// ⛔ Questa è l'unica strada che può dire di sì a una chiave: quando passa, si dichiara.
|
|
427
|
+
// Quando NON passa, il fornitore ha comunque risposto qualcosa che la riguarda: `false`.
|
|
428
|
+
credenzialeVerificata: valida,
|
|
429
|
+
motivo: valida
|
|
430
|
+
? `${etichetta}: richiesta minima di generazione riuscita; elenco modelli non verificato.`
|
|
431
|
+
: dichiaraErrore
|
|
432
|
+
? `${etichetta}: la risposta dichiara un errore, quindi la chiave non è confermata.`
|
|
433
|
+
: `${etichetta}: risposta HTTP ${risposta.status} senza un messaggio valido; credenziale non verificata.`,
|
|
434
|
+
modelli: null, millisecondi, httpStatus: risposta.status };
|
|
435
|
+
}
|
|
436
|
+
let catalogoValido = true;
|
|
437
|
+
if (record?.catalogo.forma === 'dashscope-output') {
|
|
438
|
+
try { paginaDashScope(corpo); } catch { catalogoValido = false; }
|
|
439
|
+
} else if (record?.sonda.richiedeCatalogoValido) {
|
|
440
|
+
catalogoValido = Array.isArray(corpo?.data) && corpo.data.every(m => m && typeof m.id === 'string' && m.id.trim());
|
|
441
|
+
}
|
|
442
|
+
if (!catalogoValido) {
|
|
443
|
+
return nonGiudicata({ provider, esito: 'errore', motivo: `${etichetta}: risposta HTTP ${risposta.status} senza un elenco modelli valido; credenziale non verificata.`, modelli: null, millisecondi, httpStatus: risposta.status });
|
|
444
|
+
}
|
|
445
|
+
const modelli = Number(sonda.conta(corpo));
|
|
446
|
+
return {
|
|
447
|
+
provider,
|
|
448
|
+
esito: 'collegato',
|
|
449
|
+
/*
|
|
450
|
+
* CLI-REQ-06 — il terzo stato, dichiarato anche qui:
|
|
451
|
+
* catalogo PUBBLICO → `false`: raggiunto, ma di questa chiave non dice niente (invariato);
|
|
452
|
+
* catalogo autenticato con una chiave → `true`: il fornitore l'ha accettata (è la premessa
|
|
453
|
+
* su cui questa sonda è nata — «200 = credenziale sufficiente, 401 = non autorizzata»);
|
|
454
|
+
* senza chiave (`auth: nessuna`, locali) → `null`: non c'era niente da giudicare.
|
|
455
|
+
*/
|
|
456
|
+
credenzialeVerificata: sonda.catalogoPubblico ? false : chiave ? true : null,
|
|
457
|
+
motivo: sonda.catalogoPubblico
|
|
458
|
+
? `${etichetta}: catalogo pubblico raggiunto, ${modelli} modelli visibili; chiave non verificata. La generazione non è stata provata.`
|
|
459
|
+
: Number.isFinite(modelli) && modelli >= 0
|
|
460
|
+
? `${etichetta}: catalogo raggiunto, ${modelli} modelli visibili${record?.catalogo.forma === 'dashscope-output' ? ' nella prima pagina' : ''}. La generazione non è stata provata.`
|
|
461
|
+
: `${etichetta}: credenziale accettata.`,
|
|
462
|
+
modelli: Number.isFinite(modelli) ? modelli : null,
|
|
463
|
+
millisecondi,
|
|
464
|
+
httpStatus: risposta.status,
|
|
465
|
+
};
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
async function elencaModelli(provider) {
|
|
469
|
+
if (provider === 'esterno') {
|
|
470
|
+
const runtime = runtimeAgente();
|
|
471
|
+
const nome = identitaAgente?.runtime === JSON.stringify(runtime) ? identitaAgente.nome : 'Agente esterno';
|
|
472
|
+
return { provider, fonte: 'configurazione', credenzialeVerificata: false,
|
|
473
|
+
modelli: [{ id: 'esterno:predefinito', nome, provider, toolCalling: 'ignoto', capacita: { toolCall: null, reasoning: null } }] };
|
|
474
|
+
}
|
|
475
|
+
if (!CATALOGHI_DIRETTI.includes(provider)) throw new ProviderProbeError('Catalogo diretto non disponibile.', 'PROVIDER_INVALID');
|
|
476
|
+
const record = REGISTRO_FORNITORI[provider];
|
|
477
|
+
const etichetta = record.etichetta;
|
|
478
|
+
const runtime = leggiRuntime(provider);
|
|
479
|
+
const configurati = record.cloud ? modelliConfigurati(provider, runtime) : [];
|
|
480
|
+
// P-K-bis: la preferenza non verifica credenziali né disponibilità remota.
|
|
481
|
+
if (record.cloud && provider !== 'bedrock') {
|
|
482
|
+
if (configurati.length) return { provider, fonte: 'configurazione', credenzialeVerificata: false, modelli: configurati };
|
|
483
|
+
throw new ProviderProbeError(provider === 'azure'
|
|
484
|
+
? 'Azure AI Foundry: indica il nome della distribuzione in Modelli configurati, nella pagina Fornitori e accessi.'
|
|
485
|
+
: 'Google Vertex AI: indica un modello abilitato nel progetto in Modelli configurati, nella pagina Fornitori e accessi.', 'CATALOG_CONFIGURATION_REQUIRED');
|
|
486
|
+
}
|
|
487
|
+
const key = leggiChiave(provider);
|
|
488
|
+
if (!key) throw new ProviderProbeError(`Inserisci la chiave ${etichetta} nel pannello Provider.`, 'PROVIDER_KEY_MISSING');
|
|
489
|
+
// P-J — nessun catalogo remoto documentato: nessuna generazione o GET sostitutivo.
|
|
490
|
+
// ⛔ CLI-REQ-06 — la condizione è «non ha un elenco», non «ha una richiesta minima»: i quattro
|
|
491
|
+
// fornitori a catalogo pubblico hanno entrambe le cose, e il loro elenco vero si chiede a loro.
|
|
492
|
+
if (record.sonda.richiestaMinima && record.sonda.attiva === false) {
|
|
493
|
+
const riserva = catalogoDiRiservaPer(provider);
|
|
494
|
+
return { provider, fonte: 'documentazione', credenzialeVerificata: false,
|
|
495
|
+
avviso: `${etichetta}: elenco dalla documentazione del ${record.data}; accesso ai modelli non verificato.`,
|
|
496
|
+
modelli: riserva.modelli };
|
|
497
|
+
}
|
|
498
|
+
// P-J — il wire governa auth/paginazione anche quando il nome non è «anthropic».
|
|
499
|
+
const catalogoAnthropic = record.wire === 'anthropic-messages';
|
|
500
|
+
const cloud = record.cloud ? destinazioneCloud(provider, runtime, key, null, { catalogo: true }) : null;
|
|
501
|
+
// P-K — fine
|
|
502
|
+
const headers = { Accept: 'application/json' };
|
|
503
|
+
if (catalogoAnthropic) {
|
|
504
|
+
if (record.auth.tipo === 'bearer') headers.Authorization = `Bearer ${key}`;
|
|
505
|
+
else headers[record.auth.header] = key;
|
|
506
|
+
headers['anthropic-version'] = '2023-06-01';
|
|
507
|
+
}
|
|
508
|
+
else if (provider === 'gemini') headers['x-goog-api-key'] = key;
|
|
509
|
+
else headers.Authorization = `Bearer ${key}`;
|
|
510
|
+
// P-K
|
|
511
|
+
if (cloud) Object.assign(headers, cloud.headers);
|
|
512
|
+
const signal = AbortSignal.timeout(Math.min(runtime.timeoutSeconds || 30, 30) * 1000);
|
|
513
|
+
const rows = []; const cursors = new Set(); let cursor;
|
|
514
|
+
const dashscope = record.catalogo.forma === 'dashscope-output';
|
|
515
|
+
let numeroPagina = 1; let totaleAtteso;
|
|
516
|
+
do {
|
|
517
|
+
const url = new URL(cloud?.url ?? urlDellaSonda(SONDE_PROVIDER[provider], runtime.endpoint));
|
|
518
|
+
if (dashscope) url.searchParams.set('page_no', String(numeroPagina));
|
|
519
|
+
if (provider === 'gemini') { url.searchParams.set('pageSize', '1000'); if (cursor) url.searchParams.set('pageToken', cursor); }
|
|
520
|
+
if (catalogoAnthropic) { url.searchParams.set('limit', '1000'); if (cursor) url.searchParams.set('after_id', cursor); }
|
|
521
|
+
let response;
|
|
522
|
+
try { response = await fetchImpl(url.toString(), { headers, signal, redirect: 'error' }); }
|
|
523
|
+
catch { throw new ProviderProbeError(`Catalogo ${etichetta} non raggiungibile.`, 'CATALOG_UNREACHABLE'); }
|
|
524
|
+
if (response.status === 404 && record.catalogo.ripiegoSu404 === 'documentazione') {
|
|
525
|
+
return { provider, fonte: 'documentazione', credenzialeVerificata: false,
|
|
526
|
+
avviso: `${etichetta}: catalogo remoto HTTP 404; elenco dalla documentazione del ${record.prezzi.data}, accesso ai modelli non verificato.`,
|
|
527
|
+
modelli: record.modelliNoti.map(m => ({ ...metadatiNoti(record, m.id), id: `${provider}:${m.id}`, provider, nome: `${m.nome} · catalogo documentato`, fonte: 'documentazione' })),
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
if (!response.ok) throw new ProviderProbeError(`Catalogo ${etichetta}: HTTP ${response.status}.`, 'CATALOG_UPSTREAM_ERROR');
|
|
531
|
+
let data;
|
|
532
|
+
try { data = await response.json(); }
|
|
533
|
+
catch { throw new ProviderProbeError(`Catalogo ${etichetta} non valido.`, 'CATALOG_UPSTREAM_ERROR'); }
|
|
534
|
+
if (dashscope) {
|
|
535
|
+
const pagina = paginaDashScope(data, numeroPagina);
|
|
536
|
+
if ((totaleAtteso !== undefined && totaleAtteso !== pagina.total)
|
|
537
|
+
|| rows.length + pagina.models.length > pagina.total
|
|
538
|
+
|| pagina.models.some(m => rows.some(r => r.id === m.model))) {
|
|
539
|
+
throw new ProviderProbeError('Qwen: paginazione del catalogo incoerente.', 'CATALOG_UPSTREAM_ERROR');
|
|
540
|
+
}
|
|
541
|
+
totaleAtteso = pagina.total;
|
|
542
|
+
rows.push(...pagina.models.map(m => ({ id: m.model, display_name: m.name })));
|
|
543
|
+
cursor = rows.length < totaleAtteso ? ++numeroPagina : null;
|
|
544
|
+
if (cursor > 20) throw new ProviderProbeError('Qwen: catalogo incompleto dopo venti pagine.', 'CATALOG_UPSTREAM_ERROR');
|
|
545
|
+
continue;
|
|
546
|
+
}
|
|
547
|
+
const page = provider === 'gemini' ? data?.models : data?.data;
|
|
548
|
+
if (!Array.isArray(page) || page.some(row => !row || typeof (provider === 'gemini' ? row.name : row.id) !== 'string')) throw new ProviderProbeError(`Catalogo ${etichetta} non valido.`, 'CATALOG_UPSTREAM_ERROR');
|
|
549
|
+
if (catalogoAnthropic && data.has_more === true && (typeof data.last_id !== 'string' || !data.last_id)) throw new ProviderProbeError('Paginazione del catalogo incompleta.', 'CATALOG_UPSTREAM_ERROR');
|
|
550
|
+
rows.push(...page);
|
|
551
|
+
cursor = provider === 'gemini' ? data.nextPageToken : catalogoAnthropic && data.has_more ? data.last_id : null;
|
|
552
|
+
if (cursor && (cursors.has(cursor) || cursors.size >= 20)) throw new ProviderProbeError('Paginazione del catalogo non valida.', 'CATALOG_UPSTREAM_ERROR');
|
|
553
|
+
cursors.add(cursor);
|
|
554
|
+
} while (cursor);
|
|
555
|
+
const modelli = rows.filter(row => provider === 'gemini' ? row.supportedGenerationMethods?.includes('generateContent') && !/(tts|image)/u.test(row.name) : provider === 'openai' ? /^(gpt-|chatgpt-|o[1-9])/u.test(row.id) && !/(audio|realtime|transcribe|tts|image|codex|instruct)/u.test(row.id) : typeof row.id === 'string').map(row => {
|
|
556
|
+
const id = provider === 'gemini' ? row.name.replace(/^models\//u, '') : row.id;
|
|
557
|
+
const noti = metadatiNoti(record, id);
|
|
558
|
+
return { ...noti, id: `${provider}:${id}`, nome: row.display_name || row.displayName || noti.nome || id, provider,
|
|
559
|
+
contextLength: row.max_input_tokens ?? row.inputTokenLimit ?? noti.contextLength ?? null,
|
|
560
|
+
...(row.capabilities?.image_input ? { inputModalities: row.capabilities.image_input.supported ? ['text','image'] : ['text'] } : {}),
|
|
561
|
+
};
|
|
562
|
+
});
|
|
563
|
+
// P-K-bis: conserva i dati letti dal catalogo, aggiunge soltanto gli id mancanti.
|
|
564
|
+
const presenti = new Set(modelli.map(m => m.id));
|
|
565
|
+
modelli.push(...configurati.filter(m => !presenti.has(m.id)));
|
|
566
|
+
return { provider, modelli };
|
|
567
|
+
}
|
|
568
|
+
return Object.freeze({ prova, elencaModelli });
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
/** Metadati dichiarati, mai trasformati in misure della chiamata o prezzi osservati. */
|
|
572
|
+
function metadatiNoti(record, id) {
|
|
573
|
+
const modello = record.modelliNoti?.find(m => m.id === id);
|
|
574
|
+
if (!modello) return {};
|
|
575
|
+
return { nome: modello.nome, contextLength: modello.contextLength, contestoDichiarato: modello.contestoDichiarato,
|
|
576
|
+
fonteMetadati: modello.fonte, dataMetadati: modello.data, maxOutputTokens: modello.maxOutputTokens,
|
|
577
|
+
prezzi: { ...record.prezzi, ...modello.prezzi }, ragionamento: modello.ragionamento, cache: record.cache.etichetta,
|
|
578
|
+
reasoning: { supportedEfforts: modello.ragionamento.livelli,
|
|
579
|
+
defaultEffort: modello.ragionamento.livelli.includes('max') ? 'max' : null,
|
|
580
|
+
defaultEnabled: true, mandatory: !modello.ragionamento.thinking.includes('disabled') },
|
|
581
|
+
};
|
|
582
|
+
}
|