@xopcai/xopc 0.0.361 → 0.0.363
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/dist/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{activity-store-tpFInIoh.js → activity-store-GvR2gZ0T.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-DUhFW_Go.js → agent-avatar-dicebear-DkcXyPH0.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-defaults-settings-panel-BNILmgey.js → agent-defaults-settings-panel-B-l5AaYN.js} +1 -1
- package/dist/gateway/static/root/assets/{agent-plugin-dialog-CnWci4JJ.js → agent-plugin-dialog-BY6tPYOK.js} +1 -1
- package/dist/gateway/static/root/assets/{agents-CKS5LUUs.js → agents-CdSEoeje.js} +3 -3
- package/dist/gateway/static/root/assets/{agents-admin-api-CiAjB2iQ.js → agents-admin-api-BkXb7VEZ.js} +1 -1
- package/dist/gateway/static/root/assets/{app-management-settings-panel-DEig00YU.js → app-management-settings-panel-BtD53cfe.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-CccnRRrL.js → appearance-settings-BAweWgX_.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-CkT6UNZW.js → apps-page-CS6-hrIB.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-CIlmZBe5.js → archive-plugin-CKe3qAaL.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-8kZugDcf.js → automations-page-CtE8r9Y2.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-workspace-3V54ShL0.js → automations-workspace-pnMgR4Da.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-BuQiee_b.js → binary-plugins-J51oS7-D.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-BFFRbsKX.js → block-editor-D5hf6WcP.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automation-inputs-CWujpdCx.js → browser-automation-inputs-5B3qAdmb.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-automations-page-HVXiBKXE.js → browser-automations-page-PZDQlXH4.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-DpxAXNvM.js → browser-settings-page-2YDV-ZWt.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-CngwB2JU.js → capabilities-BKu9BqPe.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-RMlKKfGF.js → capabilities-CT_ZW9fT.js} +1 -1
- package/dist/gateway/static/root/assets/{capabilities-page-BBi4ef3u.js → capabilities-page-CB-iX3Fy.js} +2 -2
- package/dist/gateway/static/root/assets/{capabilities-settings-panel-IxszGl-Q.js → capabilities-settings-panel-BCTuWNaW.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-header-actions-BLOIJvIZ.js → capability-header-actions--aC5XB9E.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-Ced5nFBc.js → channels-JnAQP1bt.js} +1 -1
- package/dist/gateway/static/root/assets/{chat-terminal-dock-BskUzDCS.js → chat-terminal-dock-BFZEpvzn.js} +1 -1
- package/dist/gateway/static/root/assets/{computer-settings-page-De-pjlvn.js → computer-settings-page-Dc9ooEM-.js} +1 -1
- package/dist/gateway/static/root/assets/{connector-service-page-Qc1mVfp9.js → connector-service-page-1_QB5D-t.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-CPqeY1x5.js → connectors-page-Um_Jy5zh.js} +1 -1
- package/dist/gateway/static/root/assets/{date-picker-7ppMIGfQ.js → date-picker-C9ZqTPui.js} +1 -1
- package/dist/gateway/static/root/assets/dependency-picker-DBz0Pmyr.js +1 -0
- package/dist/gateway/static/root/assets/{desktop-pet-GKEfqHDF.js → desktop-pet-BLtp2MmX.js} +1 -1
- package/dist/gateway/static/root/assets/{desktop-pet-settings-qoFZBKe9.js → desktop-pet-settings-BNGbqwec.js} +1 -1
- package/dist/gateway/static/root/assets/{device-pairing-wizard-D8sTDd5s.js → device-pairing-wizard-7rdo-A7d.js} +1 -1
- package/dist/gateway/static/root/assets/{directory-picker-path-field-BC87CZa2.js → directory-picker-path-field-BnilGgwM.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-debug-page-VxCq29jC.js → extension-debug-page-BGyJpRv4.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-DypBVBTt.js → extension-page-DsrVktRN.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-provider-wlvxPese.js → extension-provider-F9JzOWZ3.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-DzzkBN9y.js → extension-settings-page-jeb0ytRK.js} +1 -1
- package/dist/gateway/static/root/assets/{fallback-models-s4qTRcPJ.js → fallback-models-bZcxkEDz.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-swr-ZM72jovc.js → gateway-config-swr-CoRoOG1l.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-realtime-B-hrykag.js → gateway-realtime-D0ygm6bc.js} +1 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-DBvCRclh.js +1 -0
- package/dist/gateway/static/root/assets/{gateway-settings--M7ZH_37.js → gateway-settings-A9SzLsGp.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DjWuTj8B.js → gateway-startup-retry-FDS6S6qa.js} +1 -1
- package/dist/gateway/static/root/assets/{home-page-KFs5suD1.js → home-page-CnHzaH9K.js} +1 -1
- package/dist/gateway/static/root/assets/{image-generation-api-C9cwrqCr.js → image-generation-api-BPd76jW5.js} +1 -1
- package/dist/gateway/static/root/assets/{imports-page-BKy0ImXk.js → imports-page-CzpsV6Vz.js} +1 -1
- package/dist/gateway/static/root/assets/{index-C0JT9Ja4.js → index-BYNmHn-z.js} +7 -7
- package/dist/gateway/static/root/assets/index-CbJNcWAn.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-CexiL-Ey.js → keyboard-shortcuts-settings-D90Dvlwo.js} +1 -1
- package/dist/gateway/static/root/assets/{local-app-workbench-page-CcU1OLWQ.js → local-app-workbench-page-_9IKMkV8.js} +1 -1
- package/dist/gateway/static/root/assets/{local-apps-page-BcLijQx4.js → local-apps-page-D6gejARs.js} +1 -1
- package/dist/gateway/static/root/assets/{local-session-drafts-C7QjBMNp.js → local-session-drafts-gbemPamv.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-5sV3dDRG.js → locale-store-DId6-v2R.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-48nrZNQe.js → logs-page--t8d-oLb.js} +1 -1
- package/dist/gateway/static/root/assets/{management-settings-B_gzl_Vh.js → management-settings-DVDmXzTg.js} +1 -1
- package/dist/gateway/static/root/assets/{markdown-view-V0f5NW7D.js → markdown-view-DQZ51j9e.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-_obxSZmT.js → media-plugins-XLewwq2q.js} +1 -1
- package/dist/gateway/static/root/assets/{messages-CaURu8bb.js → messages-BHvpZNe9.js} +2 -2
- package/dist/gateway/static/root/assets/models-hub-cache-B9SKjidc.js +1 -0
- package/dist/gateway/static/root/assets/{note-detail-panel-B9AeX98W.js → note-detail-panel-DuZAEfU1.js} +3 -3
- package/dist/gateway/static/root/assets/{notes-api-CBnMq6Nc.js → notes-api-Byt65v3r.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-CKTTfxw-.js → notes-page-CgWNESGp.js} +1 -1
- package/dist/gateway/static/root/assets/{oauth-api-Bu2RoLF2.js → oauth-api-BYsCbyGZ.js} +1 -1
- package/dist/gateway/static/root/assets/{page-context-capture-button-BsxBAWD_.js → page-context-capture-button-Z72eiQZn.js} +1 -1
- package/dist/gateway/static/root/assets/{personal-page-Cfl1FR3i.js → personal-page-BtMatL9a.js} +1 -1
- package/dist/gateway/static/root/assets/{product-open-page-D1ARqS3r.js → product-open-page-w5Hdf0hQ.js} +1 -1
- package/dist/gateway/static/root/assets/{project-detail-page-Cro1OE8P.js → project-detail-page-CP6I3ZCV.js} +1 -1
- package/dist/gateway/static/root/assets/{projects-page-CdEmmBCz.js → projects-page-DYs6XrrK.js} +1 -1
- package/dist/gateway/static/root/assets/{registry-api-j1rNFkP2.js → registry-api-7F2WNEZC.js} +1 -1
- package/dist/gateway/static/root/assets/{remote-access-hub-C9_ea_4p.js → remote-access-hub-DjYoYAEw.js} +1 -1
- package/dist/gateway/static/root/assets/{runtime-tools-settings-panel-DtmQbC72.js → runtime-tools-settings-panel-Qar_DXLv.js} +1 -1
- package/dist/gateway/static/root/assets/{scenes-page-DeFdMf2q.js → scenes-page-BSIcDCa-.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-ByEHHW6n.js → schema-form-C_cJLGgF.js} +1 -1
- package/dist/gateway/static/root/assets/{send-session-input-CO_vUCb8.js → send-session-input-D1oc6BC6.js} +1 -1
- package/dist/gateway/static/root/assets/{session-api-CYPGi01d.js → session-api-N3N37MrR.js} +1 -1
- package/dist/gateway/static/root/assets/{session-manager-BdJThQQ6.js → session-manager-CMGUwtbN.js} +2 -2
- package/dist/gateway/static/root/assets/{sessions-page-CV0JlaXS.js → sessions-page-BBV5Z24a.js} +1 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-DRc9IDJf.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-CHKsZFAN.js → settings-page-F2vGXBw7.js} +1 -1
- package/dist/gateway/static/root/assets/{setup-status-panel-CC58OVOe.js → setup-status-panel-DJxbjUOg.js} +1 -1
- package/dist/gateway/static/root/assets/{share-link-dialog-ZOdnHkmb.js → share-link-dialog-TBBGJPfT.js} +1 -1
- package/dist/gateway/static/root/assets/{share-preview-page-BPDWUuH_.js → share-preview-page-DyRLPsPv.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-api-T5gR8yqz.js → shares-api-DU0fHP2G.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-DaXvdH8c.js → shares-settings-CJa6FBpP.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-api-CHCLMcfO.js → skill-api-C340XD9d.js} +1 -1
- package/dist/gateway/static/root/assets/{skill-reload-api-C-RoiUJi.js → skill-reload-api-ClyFqHk7.js} +1 -1
- package/dist/gateway/static/root/assets/{skills-page-a9UQQnqn.js → skills-page-C4lF94m5.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-Dho_dlBS.js → system-settings-panel-CmJUu3lb.js} +1 -1
- package/dist/gateway/static/root/assets/task-detail-page-De-Rr8W6.js +2 -0
- package/dist/gateway/static/root/assets/{theme-store-CEl74p7l.js → theme-store-OhcbFEUR.js} +1 -1
- package/dist/gateway/static/root/assets/tracing-settings-page-DoBJ0TYY.js +1 -0
- package/dist/gateway/static/root/assets/{tunnel-api-BDqCyf8G.js → tunnel-api-Bl4nrCuE.js} +1 -1
- package/dist/gateway/static/root/assets/{usage-page-DKwKfszc.js → usage-page-QZEAaa8n.js} +1 -1
- package/dist/gateway/static/root/assets/{use-autosave-D09eZTLp.js → use-autosave-veoM0dHn.js} +1 -1
- package/dist/gateway/static/root/assets/{use-composer-attachments-CkvJM8Km.js → use-composer-attachments-DB0bBGzZ.js} +1 -1
- package/dist/gateway/static/root/assets/{use-file-preview-expanded-J3HnG8OH.js → use-file-preview-expanded-Bope6MW6.js} +2 -2
- package/dist/gateway/static/root/assets/{user-model-page-BsgADrGL.js → user-model-page-Crh2oNpZ.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-DH-to9Rd.js → voice-api-key-field-CwD6a3KQ.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-config-api-CmCfVy3H.js → voice-config-api-Daexehmc.js} +1 -1
- package/dist/gateway/static/root/assets/{work-discovery-overlay-x1vZDBhu.js → work-discovery-overlay-9jnjEAaz.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-DGatvmK1.js → workflow-run-setup-panel-BAcPxdcT.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-DgNy6mBQ.js → workflows-page-D8Zm-ooa.js} +1 -1
- package/dist/gateway/static/root/assets/{workspace-api-DzPw92Kx.js → workspace-api-Cs4sjV2A.js} +2 -2
- package/dist/gateway/static/root/connector-icons/README.md +5 -2
- package/dist/gateway/static/root/connector-icons/SOURCES.md +15 -1
- package/dist/gateway/static/root/connector-icons/amap.ico +0 -0
- package/dist/gateway/static/root/connector-icons/antv-chart.png +0 -0
- package/dist/gateway/static/root/connector-icons/bailian.png +0 -0
- package/dist/gateway/static/root/connector-icons/dida365.png +0 -0
- package/dist/gateway/static/root/connector-icons/flomo.png +0 -0
- package/dist/gateway/static/root/connector-icons/gmail.svg +1 -3
- package/dist/gateway/static/root/connector-icons/jira.svg +3 -14
- package/dist/gateway/static/root/connector-icons/railway-12306.jpg +0 -0
- package/dist/gateway/static/root/connector-icons/railway-query.svg +1 -0
- package/dist/gateway/static/root/connector-icons/tencent-docs.ico +0 -0
- package/dist/gateway/static/root/connector-icons/yuque.png +0 -0
- package/dist/gateway/static/root/index.html +28 -28
- package/dist/package.js +1 -1
- package/dist/packages/gateway-contract/src/task-result-delivery.js +2 -2
- package/dist/src/agent/agent-manager.js +10 -6
- package/dist/src/agent/embedded/model-runtime.js +1 -1
- package/dist/src/agent/embedded/run-for-session.js +28 -3
- package/dist/src/agent/embedded/run-turn.js +18 -3
- package/dist/src/agent/image/tool-model-config.js +1 -1
- package/dist/src/agent/mcp/oauth/mcp-oauth-store.js +1 -1
- package/dist/src/agent/service.js +5 -3
- package/dist/src/agent/skills/hub-lock.js +1 -1
- package/dist/src/agent/tasks/task-judge-service.js +1 -1
- package/dist/src/agent-catalog/migrations/legacy-config-cutover.js +1 -1
- package/dist/src/agent-catalog/repository.js +1 -1
- package/dist/src/auth/credentials.js +2 -2
- package/dist/src/auth/oauth-provider-lock.js +1 -1
- package/dist/src/auth/profiles/store.js +1 -1
- package/dist/src/auth/sync-provider-auth.js +1 -1
- package/dist/src/browser/providers/browser-ext-install.js +1 -1
- package/dist/src/chat-commands/agent-edit.js +1 -1
- package/dist/src/chat-commands/builtins/config.js +1 -1
- package/dist/src/cli/commands/agent.js +1 -1
- package/dist/src/cli/commands/doctor/checks/database-schema.js +1 -1
- package/dist/src/cli/commands/doctor/checks/image-providers.js +1 -1
- package/dist/src/cli/commands/doctor/checks/provider-auth.js +3 -3
- package/dist/src/cli/commands/models.js +1 -1
- package/dist/src/cli/commands/onboard/model.js +3 -3
- package/dist/src/config/agent-profile.js +1 -1
- package/dist/src/config/index.js +4 -4
- package/dist/src/config/loader.js +2 -2
- package/dist/src/config/models-json.js +1 -1
- package/dist/src/config/reload.d.ts +1 -0
- package/dist/src/config/reload.js +4 -0
- package/dist/src/config/rules.js +5 -0
- package/dist/src/config/schema.d.ts +23 -0
- package/dist/src/config/schema.js +6 -3
- package/dist/src/connectors/china-catalog.js +572 -0
- package/dist/src/connectors/cli/authorization.js +3 -2
- package/dist/src/connectors/cli/installer.js +3 -1
- package/dist/src/extensions/activation-context.js +1 -1
- package/dist/src/extensions/lockfile.js +1 -1
- package/dist/src/gateway/file-path-classifier.js +1 -1
- package/dist/src/gateway/hono/lib/config-payload.js +2 -2
- package/dist/src/gateway/hono/routes/agents.js +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +2 -2
- package/dist/src/gateway/hono/routes/lazy-bundles.js +8 -0
- package/dist/src/gateway/hono/routes/models.js +2 -2
- package/dist/src/gateway/hono/routes/observability.d.ts +3 -0
- package/dist/src/gateway/hono/routes/observability.js +106 -0
- package/dist/src/gateway/image-generation-setup.js +1 -1
- package/dist/src/gateway/lock.js +1 -1
- package/dist/src/gateway/security/gateway-scopes.js +1 -0
- package/dist/src/gateway/service/config-coordinator.d.ts +0 -25
- package/dist/src/gateway/service/config-coordinator.js +31 -0
- package/dist/src/gateway/service.js +5 -3
- package/dist/src/gateway/side-chat/manager.js +3 -2
- package/dist/src/observability/config.d.ts +40 -0
- package/dist/src/observability/config.js +31 -0
- package/dist/src/observability/langfuse.d.ts +31 -0
- package/dist/src/observability/langfuse.js +180 -0
- package/dist/src/observability/runtime.d.ts +57 -0
- package/dist/src/observability/runtime.js +404 -0
- package/dist/src/observability/sanitize.d.ts +3 -0
- package/dist/src/observability/sanitize.js +74 -0
- package/dist/src/observability/store.d.ts +20 -0
- package/dist/src/observability/store.js +128 -0
- package/dist/src/observability/storeWorker.d.ts +2 -0
- package/dist/src/observability/storeWorker.js +155 -0
- package/dist/src/observability/types.d.ts +21 -0
- package/dist/src/observability/types.js +1 -0
- package/dist/src/platform/index.js +1 -1
- package/dist/src/providers/auth-runtime/resolve-auth.js +1 -1
- package/dist/src/providers/index.js +3 -3
- package/dist/src/providers/model-call.js +19 -5
- package/dist/src/providers/model-catalog-persistence.js +1 -1
- package/dist/src/providers/model-registry.js +1 -1
- package/dist/src/providers/provider-disconnect-core.js +1 -1
- package/dist/src/providers/xopc-cloud-catalog-coordinator.js +1 -1
- package/dist/src/storage/sqlite/ai-usage-repository.d.ts +6 -0
- package/dist/src/storage/sqlite/ai-usage-repository.js +4 -2
- package/dist/src/storage/sqlite/migrations/233_observability_usage_links.sql +3 -0
- package/dist/src/storage/sqlite/migrations/runner.d.ts +1 -1
- package/dist/src/storage/sqlite/migrations/runner.js +3 -3
- package/dist/src/storage/sqlite/schema.js +1 -1
- package/dist/src/tasks/task-result-delivery-repository.js +4 -5
- package/dist/src/tui/backends/embedded-backend.js +2 -2
- package/dist/src/tui/tui-commands.js +1 -1
- package/dist/src/tui/tui-startup-resources.js +1 -1
- package/dist/src/tui/tui.js +1 -1
- package/dist/src/usage/recorder.d.ts +4 -2
- package/dist/src/usage/recorder.js +54 -26
- package/dist/src/usage/types.d.ts +3 -0
- package/dist/src/utils/logger/context.d.ts +1 -1
- package/dist/src/utils/logger/context.js +4 -1
- package/dist/src/utils/logger/types.d.ts +3 -0
- package/package.json +5 -2
- package/dist/gateway/static/root/assets/dependency-picker-DfYC22si.js +0 -1
- package/dist/gateway/static/root/assets/gateway-realtime-store-B6B1fw_K.js +0 -1
- package/dist/gateway/static/root/assets/index-B7AHDHSO.css +0 -1
- package/dist/gateway/static/root/assets/models-hub-cache-Cs3E5pEA.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-D9sW0hKa.js +0 -1
- package/dist/gateway/static/root/assets/task-detail-page-B5JyH4z7.js +0 -1
|
@@ -297,6 +297,14 @@ const AUTHENTICATED_LAZY_ROUTE_BUNDLES = [
|
|
|
297
297
|
return { register: registerLogsRoutes };
|
|
298
298
|
}
|
|
299
299
|
},
|
|
300
|
+
{
|
|
301
|
+
id: "observability",
|
|
302
|
+
match: (path) => startsWithAny(path, ["/api/observability"]),
|
|
303
|
+
load: async () => {
|
|
304
|
+
const { registerObservabilityRoutes } = await import("./observability.js");
|
|
305
|
+
return { register: registerObservabilityRoutes };
|
|
306
|
+
}
|
|
307
|
+
},
|
|
300
308
|
{
|
|
301
309
|
id: "usage",
|
|
302
310
|
match: (path) => startsWithAny(path, ["/api/usage"]),
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { resolveEffectiveAgentConfigForAgent } from "../../../config/agent-profile.js";
|
|
2
1
|
import { resolveModelsJsonPath } from "../../../config/paths.js";
|
|
2
|
+
import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
|
|
3
|
+
import { resolveEffectiveAgentConfigForAgent } from "../../../config/agent-profile.js";
|
|
3
4
|
import { init_resolve_config_value, testApiKeyResolution } from "../../../config/resolve-config-value.js";
|
|
4
5
|
import { computerModelProfile, init_model_policy, isDedicatedComputerModel } from "../../../computer/model-policy.js";
|
|
5
6
|
import { init_models_json, loadModelsJson, saveModelsJson, validateModelsConfig } from "../../../config/models-json.js";
|
|
6
7
|
import { getModelCatalogStore, init_model_catalog_store } from "../../../providers/model-catalog-store.js";
|
|
7
8
|
import { getModelRegistry } from "../../../providers/model-registry.js";
|
|
8
|
-
import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
|
|
9
9
|
import "../../../providers/constants.js";
|
|
10
10
|
import { getProviderRegistry, init_plugin_registry } from "../../../providers/plugin-registry.js";
|
|
11
11
|
import { PROVIDER_META, getAllModels, getAllProviders, getAvailableModels, getProviderAuthState, init_providers, isProviderConfigured, isProviderConfiguredSync } from "../../../providers/index.js";
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { CredentialResolver, init_credentials } from "../../../auth/credentials.js";
|
|
2
|
+
import { TracingConfigSchema, init_config } from "../../../observability/config.js";
|
|
3
|
+
import { configureTracing, localStore, testLangfuse, tracingConfig, tracingStatus } from "../../../observability/runtime.js";
|
|
4
|
+
import { z } from "zod";
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
//#region src/gateway/hono/routes/observability.ts
|
|
7
|
+
init_credentials();
|
|
8
|
+
init_config();
|
|
9
|
+
const traceIdSchema = z.string().regex(/^[0-9a-f]{32}$/);
|
|
10
|
+
const querySchema = z.object({
|
|
11
|
+
limit: z.coerce.number().int().min(1).max(100).optional(),
|
|
12
|
+
cursor: z.string().regex(/^\d{1,16}:[0-9a-f]{32}$/).optional(),
|
|
13
|
+
from: z.coerce.number().int().nonnegative().optional(),
|
|
14
|
+
to: z.coerce.number().int().positive().optional(),
|
|
15
|
+
status: z.enum([
|
|
16
|
+
"running",
|
|
17
|
+
"success",
|
|
18
|
+
"error",
|
|
19
|
+
"cancelled",
|
|
20
|
+
"suspended",
|
|
21
|
+
"interrupted"
|
|
22
|
+
]).optional(),
|
|
23
|
+
conversationId: z.string().max(200).optional(),
|
|
24
|
+
runId: z.string().max(200).optional(),
|
|
25
|
+
agentId: z.string().max(200).optional()
|
|
26
|
+
});
|
|
27
|
+
function registerObservabilityRoutes(authenticated, { service, strictRateLimitMiddleware }) {
|
|
28
|
+
const app = new Hono();
|
|
29
|
+
app.onError((error, c) => {
|
|
30
|
+
if (error instanceof z.ZodError || error instanceof SyntaxError) return c.json({ error: "Invalid tracing request" }, 400);
|
|
31
|
+
return c.json({ error: "Tracing service unavailable" }, 503);
|
|
32
|
+
});
|
|
33
|
+
app.get("/tracing/settings", async (c) => {
|
|
34
|
+
const resolver = new CredentialResolver();
|
|
35
|
+
const secret = process.env.LANGFUSE_SECRET_KEY || await resolver.revealGatewayStoredApiKey("langfuse");
|
|
36
|
+
const pub = process.env.LANGFUSE_PUBLIC_KEY || await resolver.revealGatewayStoredApiKey("langfuse-public");
|
|
37
|
+
return c.json({
|
|
38
|
+
config: tracingConfig(),
|
|
39
|
+
credentials: {
|
|
40
|
+
publicKeyConfigured: Boolean(pub),
|
|
41
|
+
secretKeyConfigured: Boolean(secret),
|
|
42
|
+
publicKeySource: process.env.LANGFUSE_PUBLIC_KEY ? "environment" : pub ? "stored" : "none",
|
|
43
|
+
secretKeySource: process.env.LANGFUSE_SECRET_KEY ? "environment" : secret ? "stored" : "none",
|
|
44
|
+
baseUrlSource: process.env.LANGFUSE_BASE_URL ? "environment" : "config"
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
app.patch("/tracing/settings", strictRateLimitMiddleware, async (c) => {
|
|
49
|
+
const parsed = TracingConfigSchema.parse(await c.req.json());
|
|
50
|
+
if (parsed.capture === "detailed") parsed.detailedUntil = Date.now() + 1800 * 1e3;
|
|
51
|
+
else delete parsed.detailedUntil;
|
|
52
|
+
const next = {
|
|
53
|
+
...service.currentConfig,
|
|
54
|
+
observability: { tracing: parsed }
|
|
55
|
+
};
|
|
56
|
+
if (!(await service.saveConfig(next)).saved) return c.json({ error: "Could not save tracing configuration" }, 500);
|
|
57
|
+
await configureTracing(parsed);
|
|
58
|
+
return c.json({
|
|
59
|
+
ok: true,
|
|
60
|
+
config: parsed
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
app.put("/tracing/langfuse/credentials", strictRateLimitMiddleware, async (c) => {
|
|
64
|
+
const body = z.object({
|
|
65
|
+
publicKey: z.string().trim().min(1).max(512).optional(),
|
|
66
|
+
secretKey: z.string().trim().min(1).max(512).optional()
|
|
67
|
+
}).strict().parse(await c.req.json());
|
|
68
|
+
const resolver = new CredentialResolver();
|
|
69
|
+
if (body.publicKey) await resolver.saveApiKey("langfuse-public", body.publicKey, { profileName: "default" });
|
|
70
|
+
if (body.secretKey) await resolver.saveApiKey("langfuse", body.secretKey, { profileName: "default" });
|
|
71
|
+
await configureTracing(service.currentConfig.observability?.tracing);
|
|
72
|
+
return c.json({ ok: true });
|
|
73
|
+
});
|
|
74
|
+
app.delete("/tracing/langfuse/credentials", strictRateLimitMiddleware, async (c) => {
|
|
75
|
+
const resolver = new CredentialResolver();
|
|
76
|
+
await resolver.deleteProviderCredential("langfuse");
|
|
77
|
+
await resolver.deleteProviderCredential("langfuse-public");
|
|
78
|
+
await configureTracing(service.currentConfig.observability?.tracing);
|
|
79
|
+
return c.json({ ok: true });
|
|
80
|
+
});
|
|
81
|
+
app.post("/tracing/langfuse/test", strictRateLimitMiddleware, async (c) => c.json({ ok: await testLangfuse() }));
|
|
82
|
+
app.get("/tracing/status", async (c) => c.json(await tracingStatus()));
|
|
83
|
+
app.get("/traces", async (c) => c.json(await localStore().request("list", querySchema.parse(c.req.query()))));
|
|
84
|
+
app.post("/traces/prune", strictRateLimitMiddleware, async (c) => {
|
|
85
|
+
await localStore().request("prune");
|
|
86
|
+
return c.json({ ok: true });
|
|
87
|
+
});
|
|
88
|
+
app.delete("/traces", strictRateLimitMiddleware, async (c) => {
|
|
89
|
+
await localStore().clear();
|
|
90
|
+
return c.json({ ok: true });
|
|
91
|
+
});
|
|
92
|
+
app.get("/traces/:traceId/export", async (c) => {
|
|
93
|
+
const id = traceIdSchema.parse(c.req.param("traceId"));
|
|
94
|
+
const data = await localStore().request("detail", id);
|
|
95
|
+
if (!data) return c.json({ error: "Trace not found" }, 404);
|
|
96
|
+
c.header("Content-Disposition", `attachment; filename="trace-${id}.json"`);
|
|
97
|
+
return c.json(data);
|
|
98
|
+
});
|
|
99
|
+
app.get("/traces/:traceId", async (c) => {
|
|
100
|
+
const data = await localStore().request("detail", traceIdSchema.parse(c.req.param("traceId")));
|
|
101
|
+
return data ? c.json(data) : c.json({ error: "Trace not found" }, 404);
|
|
102
|
+
});
|
|
103
|
+
authenticated.route("/api/observability", app);
|
|
104
|
+
}
|
|
105
|
+
//#endregion
|
|
106
|
+
export { registerObservabilityRoutes };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { normalizeAgentId } from "../agent-catalog/id.js";
|
|
2
2
|
import { AgentCatalogRepository, init_repository } from "../agent-catalog/repository.js";
|
|
3
|
-
import { ConfigSchema, init_schema } from "../config/schema.js";
|
|
4
3
|
import { init_agent_scope } from "../agent/agent-scope.js";
|
|
4
|
+
import { ConfigSchema, init_schema } from "../config/schema.js";
|
|
5
5
|
import { resolveEffectiveAgentConfigForAgent } from "../config/agent-profile.js";
|
|
6
6
|
import { fetchWithTimeoutGuarded } from "../media-shared/http/provider-http.js";
|
|
7
7
|
import "../media-shared/http/index.js";
|
package/dist/src/gateway/lock.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import path from "node:path";
|
|
2
2
|
import fs from "node:fs";
|
|
3
3
|
import { createHash } from "node:crypto";
|
|
4
|
-
import net from "node:net";
|
|
5
4
|
import fs$1 from "node:fs/promises";
|
|
5
|
+
import net from "node:net";
|
|
6
6
|
import { homedir } from "os";
|
|
7
7
|
//#region src/gateway/lock.ts
|
|
8
8
|
/**
|
|
@@ -24,6 +24,7 @@ function requiredGatewayScope(method, path) {
|
|
|
24
24
|
if (method === "GET" && path === "/api/connectors/approvals") return "sessions.read";
|
|
25
25
|
if (method === "POST" && path === "/api/connectors/approvals/respond") return "sessions.write";
|
|
26
26
|
if (path === "/api/agent" || path.startsWith("/api/agent/")) return "agents.run";
|
|
27
|
+
if (path === "/api/observability" || path.startsWith("/api/observability/")) return "gateway.admin";
|
|
27
28
|
if (path.startsWith("/api/usage")) return "gateway.admin";
|
|
28
29
|
if (path.startsWith("/api/agents") || path.startsWith("/api/models")) return methodScope(method, "agents.read", "gateway.admin");
|
|
29
30
|
if (path.startsWith("/api/sessions") || path.startsWith("/api/side-chats")) return methodScope(method, "sessions.read", "sessions.write");
|
|
@@ -1,28 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* GatewayConfigCoordinator — owns config persistence, hot-reload, and the
|
|
3
|
-
* per-section reload handlers.
|
|
4
|
-
*
|
|
5
|
-
* Was 350 lines of `GatewayService` covering nine concerns:
|
|
6
|
-
* - manual `reloadConfig()` (CLI/UI trigger)
|
|
7
|
-
* - `saveConfig()` / `updateConfig()` (PATCH /api/config)
|
|
8
|
-
* - `setExtensionActivationTarget` (extension lifecycle)
|
|
9
|
-
* - `afterWeixinCredentialsPersisted` / `afterFeishuCredentialsPersisted`
|
|
10
|
-
* (QR-login follow-ups that bypass the watcher)
|
|
11
|
-
* - `ConfigHotReloader` (fs.watch → debounced per-section dispatch)
|
|
12
|
-
* - Section reload handlers (models / agents / channels /
|
|
13
|
-
* tools / mcp / extensions)
|
|
14
|
-
* - `scheduleChannelPluginsAfterPersist` (coalesces rapid saves so
|
|
15
|
-
* Telegram/Weixin do not stop/start repeatedly)
|
|
16
|
-
*
|
|
17
|
-
* Pulled out so the gateway composition root stays focused on lifecycle, and
|
|
18
|
-
* each handler is reachable from one place when adding a new config section.
|
|
19
|
-
*
|
|
20
|
-
* **Config state ownership.** `GatewayService.config` is still the single
|
|
21
|
-
* source of truth — this coordinator reads it via `getConfig()` and writes it
|
|
22
|
-
* back via `setConfig()` after every reload / persist. We pass through rather
|
|
23
|
-
* than holding our own copy so other coordinators (sessions, marketplace,
|
|
24
|
-
* agent runner) see the latest config the moment a reload commits.
|
|
25
|
-
*/
|
|
26
1
|
import type { Config } from '../../config/schema.js';
|
|
27
2
|
import type { AgentService } from '../../agent/service.js';
|
|
28
3
|
import type { ChannelManager } from '../../channels/manager.js';
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { createLogger } from "../../utils/logger/index.js";
|
|
2
2
|
import { init_logger } from "../../utils/logger.js";
|
|
3
|
+
import { configureTracing } from "../../observability/runtime.js";
|
|
3
4
|
import { getModelRegistry } from "../../providers/model-registry.js";
|
|
4
5
|
import { init_providers } from "../../providers/index.js";
|
|
5
6
|
import { disposeAllSessionMcpRuntimes } from "../../agent/mcp/bundle-mcp-runtime.js";
|
|
@@ -11,6 +12,31 @@ import "../../config/index.js";
|
|
|
11
12
|
import { computeExtensionActivationPatch } from "../../extensions/bundled-extension-activation.js";
|
|
12
13
|
import { sanitizeTunnelConfig } from "../../tunnel/tunnel-config.js";
|
|
13
14
|
//#region src/gateway/service/config-coordinator.ts
|
|
15
|
+
/**
|
|
16
|
+
* GatewayConfigCoordinator — owns config persistence, hot-reload, and the
|
|
17
|
+
* per-section reload handlers.
|
|
18
|
+
*
|
|
19
|
+
* Was 350 lines of `GatewayService` covering nine concerns:
|
|
20
|
+
* - manual `reloadConfig()` (CLI/UI trigger)
|
|
21
|
+
* - `saveConfig()` / `updateConfig()` (PATCH /api/config)
|
|
22
|
+
* - `setExtensionActivationTarget` (extension lifecycle)
|
|
23
|
+
* - `afterWeixinCredentialsPersisted` / `afterFeishuCredentialsPersisted`
|
|
24
|
+
* (QR-login follow-ups that bypass the watcher)
|
|
25
|
+
* - `ConfigHotReloader` (fs.watch → debounced per-section dispatch)
|
|
26
|
+
* - Section reload handlers (models / agents / channels /
|
|
27
|
+
* tools / mcp / extensions)
|
|
28
|
+
* - `scheduleChannelPluginsAfterPersist` (coalesces rapid saves so
|
|
29
|
+
* Telegram/Weixin do not stop/start repeatedly)
|
|
30
|
+
*
|
|
31
|
+
* Pulled out so the gateway composition root stays focused on lifecycle, and
|
|
32
|
+
* each handler is reachable from one place when adding a new config section.
|
|
33
|
+
*
|
|
34
|
+
* **Config state ownership.** `GatewayService.config` is still the single
|
|
35
|
+
* source of truth — this coordinator reads it via `getConfig()` and writes it
|
|
36
|
+
* back via `setConfig()` after every reload / persist. We pass through rather
|
|
37
|
+
* than holding our own copy so other coordinators (sessions, marketplace,
|
|
38
|
+
* agent runner) see the latest config the moment a reload commits.
|
|
39
|
+
*/
|
|
14
40
|
init_providers();
|
|
15
41
|
init_logger();
|
|
16
42
|
const log = createLogger("GatewayConfigCoordinator");
|
|
@@ -26,6 +52,10 @@ var GatewayConfigCoordinator = class {
|
|
|
26
52
|
startHotReloader() {
|
|
27
53
|
if (this.configReloader) return;
|
|
28
54
|
this.configReloader = new ConfigHotReloader(this.opts.configPath, this.opts.getConfig(), {
|
|
55
|
+
onTracingReload: async (next) => {
|
|
56
|
+
this.opts.setConfig(next);
|
|
57
|
+
await configureTracing(next.observability?.tracing);
|
|
58
|
+
},
|
|
29
59
|
onModelsReload: (newConfig) => this.handleModelsReload(newConfig),
|
|
30
60
|
onChannelsReload: (newConfig) => this.handleChannelsReload(newConfig),
|
|
31
61
|
onCronReload: (newConfig) => this.handleAutomationReload(newConfig),
|
|
@@ -316,6 +346,7 @@ var GatewayConfigCoordinator = class {
|
|
|
316
346
|
await saveConfig(configToWrite, this.opts.configPath);
|
|
317
347
|
const reloaded = loadConfig(this.opts.configPath);
|
|
318
348
|
this.opts.setConfig(reloaded);
|
|
349
|
+
await configureTracing(reloaded.observability?.tracing);
|
|
319
350
|
if (sanitizeTunnelConfig(reloaded)) await saveConfig(reloaded, this.opts.configPath);
|
|
320
351
|
this.opts.getAgentService().applyRuntimeConfiguration(reloaded);
|
|
321
352
|
await this.opts.reconcileMemoryMaintenanceAutomations().catch((err) => {
|
|
@@ -17,14 +17,15 @@ import { init_session_repository, listSessionMetadata, patchSessionMetadata } fr
|
|
|
17
17
|
import { getSessionConfig, init_config_repository } from "../storage/sqlite/config-repository.js";
|
|
18
18
|
import { init_sqlite } from "../storage/sqlite/index.js";
|
|
19
19
|
import { normalizeAgentId } from "../agent-catalog/id.js";
|
|
20
|
+
import { init_agent_scope, listAgentEntries, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
21
|
+
import { init_paths, resolveConfigPath, resolveExtensionsDir } from "../config/paths.js";
|
|
22
|
+
import { flushTracing } from "../observability/runtime.js";
|
|
20
23
|
import { init_config, resolveKnowledgeReadPolicy } from "../user-context/config.js";
|
|
21
24
|
import { getAgentDefaultModelRef, init_schema } from "../config/schema.js";
|
|
22
|
-
import { init_agent_scope, listAgentEntries, resolveAgentWorkspaceDir, resolveDefaultAgentId } from "../agent/agent-scope.js";
|
|
23
25
|
import { init_agent_session_key, resolveAgentMainConversationId } from "../routing/agent-session-key.js";
|
|
24
26
|
import { init_session_key, resolveConversationId, sanitizeSegment } from "../routing/session-key.js";
|
|
25
27
|
import { getDefaultAgentId, init_resolve_route } from "../routing/resolve-route.js";
|
|
26
28
|
import { resolveEffectiveAgentProfileForSession } from "../config/agent-profile.js";
|
|
27
|
-
import { init_paths, resolveConfigPath, resolveExtensionsDir } from "../config/paths.js";
|
|
28
29
|
import { prewarmModelRegistry } from "../providers/model-registry.js";
|
|
29
30
|
import { init_providers, providers_exports } from "../providers/index.js";
|
|
30
31
|
import { AI_USAGE_RETENTION_MS, markStaleAiUsageEventsUnknown, pruneAiUsageEvents } from "../storage/sqlite/ai-usage-repository.js";
|
|
@@ -1513,6 +1514,7 @@ var GatewayService = class {
|
|
|
1513
1514
|
}
|
|
1514
1515
|
}
|
|
1515
1516
|
async stop() {
|
|
1517
|
+
await flushTracing();
|
|
1516
1518
|
if (!this.running) return;
|
|
1517
1519
|
this.stopping = true;
|
|
1518
1520
|
this.taskRunDispatcher?.stop();
|
|
@@ -1563,7 +1565,7 @@ var GatewayService = class {
|
|
|
1563
1565
|
await disposeAllSessionMcpRuntimes().catch((err) => {
|
|
1564
1566
|
log.warn({ err }, "MCP runtime shutdown failed");
|
|
1565
1567
|
});
|
|
1566
|
-
this._agentService?.stop();
|
|
1568
|
+
await this._agentService?.stop();
|
|
1567
1569
|
this.running = false;
|
|
1568
1570
|
this.bus.shutdown();
|
|
1569
1571
|
this.lastDeferredChannelConnectIds = [];
|
|
@@ -6,7 +6,8 @@ import { createSideChatContextSnapshot, formatSideChatSelections, validateSideCh
|
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
//#region src/gateway/side-chat/manager.ts
|
|
8
8
|
init_logger();
|
|
9
|
-
const DEFAULT_IDLE_TTL_MS =
|
|
9
|
+
const DEFAULT_IDLE_TTL_MS = 360 * 60 * 1e3;
|
|
10
|
+
const EXPIRED_RECORD_RETENTION_MS = 1800 * 1e3;
|
|
10
11
|
const DEFAULT_MAX_PER_CLIENT = 5;
|
|
11
12
|
const DEFAULT_MAX_TOTAL = 50;
|
|
12
13
|
const MAX_EXPIRED_RECORDS = 500;
|
|
@@ -260,7 +261,7 @@ var EphemeralSideChatManager = class {
|
|
|
260
261
|
this.expired.set(entry.id, {
|
|
261
262
|
clientInstanceId: entry.clientInstanceId,
|
|
262
263
|
reason,
|
|
263
|
-
removeAt: this.now() +
|
|
264
|
+
removeAt: this.now() + EXPIRED_RECORD_RETENTION_MS
|
|
264
265
|
});
|
|
265
266
|
this.pruneExpiredRecords(this.now());
|
|
266
267
|
const cleanup = this.disposeEntry(entry.id, entry.clientInstanceId);
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
export declare const TracingConfigSchema: z.ZodObject<{
|
|
3
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
4
|
+
capture: z.ZodDefault<z.ZodEnum<{
|
|
5
|
+
metadata: "metadata";
|
|
6
|
+
redacted: "redacted";
|
|
7
|
+
detailed: "detailed";
|
|
8
|
+
}>>;
|
|
9
|
+
detailedUntil: z.ZodOptional<z.ZodNumber>;
|
|
10
|
+
local: z.ZodPrefault<z.ZodObject<{
|
|
11
|
+
retentionDays: z.ZodDefault<z.ZodNumber>;
|
|
12
|
+
maxStoreMiB: z.ZodDefault<z.ZodNumber>;
|
|
13
|
+
maxTraces: z.ZodDefault<z.ZodNumber>;
|
|
14
|
+
maxSpans: z.ZodDefault<z.ZodNumber>;
|
|
15
|
+
maxTraceKiB: z.ZodDefault<z.ZodNumber>;
|
|
16
|
+
maxWriteMiBPerMinute: z.ZodDefault<z.ZodNumber>;
|
|
17
|
+
}, z.core.$strip>>;
|
|
18
|
+
langfuse: z.ZodPrefault<z.ZodObject<{
|
|
19
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
20
|
+
baseUrl: z.ZodDefault<z.ZodString>;
|
|
21
|
+
}, z.core.$strip>>;
|
|
22
|
+
}, z.core.$strict>;
|
|
23
|
+
export type TracingConfig = z.infer<typeof TracingConfigSchema>;
|
|
24
|
+
export declare const defaultTracingConfig: () => {
|
|
25
|
+
enabled: boolean;
|
|
26
|
+
capture: "metadata" | "redacted" | "detailed";
|
|
27
|
+
local: {
|
|
28
|
+
retentionDays: number;
|
|
29
|
+
maxStoreMiB: number;
|
|
30
|
+
maxTraces: number;
|
|
31
|
+
maxSpans: number;
|
|
32
|
+
maxTraceKiB: number;
|
|
33
|
+
maxWriteMiBPerMinute: number;
|
|
34
|
+
};
|
|
35
|
+
langfuse: {
|
|
36
|
+
enabled: boolean;
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
};
|
|
39
|
+
detailedUntil?: number;
|
|
40
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { __esmMin } from "../../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
//#region src/observability/config.ts
|
|
4
|
+
var TracingConfigSchema, defaultTracingConfig;
|
|
5
|
+
var init_config = __esmMin((() => {
|
|
6
|
+
TracingConfigSchema = z.object({
|
|
7
|
+
enabled: z.boolean().default(true),
|
|
8
|
+
capture: z.enum([
|
|
9
|
+
"metadata",
|
|
10
|
+
"redacted",
|
|
11
|
+
"detailed"
|
|
12
|
+
]).default("redacted"),
|
|
13
|
+
detailedUntil: z.number().int().nonnegative().optional(),
|
|
14
|
+
local: z.object({
|
|
15
|
+
retentionDays: z.number().int().min(1).max(90).default(7),
|
|
16
|
+
maxStoreMiB: z.number().int().min(16).max(4096).default(256),
|
|
17
|
+
maxTraces: z.number().int().min(10).max(1e5).default(1e4),
|
|
18
|
+
maxSpans: z.number().int().min(100).max(1e6).default(1e5),
|
|
19
|
+
maxTraceKiB: z.number().int().min(16).max(4096).default(1024),
|
|
20
|
+
maxWriteMiBPerMinute: z.number().int().min(1).max(100).default(2)
|
|
21
|
+
}).prefault({}),
|
|
22
|
+
langfuse: z.object({
|
|
23
|
+
enabled: z.boolean().default(false),
|
|
24
|
+
baseUrl: z.string().url().max(2048).refine((value) => ["http:", "https:"].includes(new URL(value).protocol), "HTTP(S) URL required").default("https://cloud.langfuse.com")
|
|
25
|
+
}).prefault({})
|
|
26
|
+
}).strict();
|
|
27
|
+
defaultTracingConfig = () => TracingConfigSchema.parse({});
|
|
28
|
+
}));
|
|
29
|
+
//#endregion
|
|
30
|
+
init_config();
|
|
31
|
+
export { TracingConfigSchema, defaultTracingConfig, init_config };
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TraceRecord } from './types.js';
|
|
2
|
+
/** Bounded OTLP/HTTP JSON exporter. No hidden SDK queue or durable replay. */
|
|
3
|
+
export declare class LangfuseTarget {
|
|
4
|
+
private baseUrl;
|
|
5
|
+
private publicKey;
|
|
6
|
+
private secretKey;
|
|
7
|
+
private queue;
|
|
8
|
+
private bytes;
|
|
9
|
+
private timer?;
|
|
10
|
+
private sending?;
|
|
11
|
+
private controller?;
|
|
12
|
+
private closed;
|
|
13
|
+
private lastWarning;
|
|
14
|
+
dropped: number;
|
|
15
|
+
lastSuccess?: number;
|
|
16
|
+
lastFailure?: number;
|
|
17
|
+
lastError?: string;
|
|
18
|
+
constructor(baseUrl: string, publicKey: string, secretKey: string);
|
|
19
|
+
status(): {
|
|
20
|
+
state: string;
|
|
21
|
+
pending: number;
|
|
22
|
+
dropped: number;
|
|
23
|
+
lastSuccess: number;
|
|
24
|
+
lastFailure: number;
|
|
25
|
+
lastError: string;
|
|
26
|
+
};
|
|
27
|
+
enqueue(record: TraceRecord): void;
|
|
28
|
+
flush(): Promise<void>;
|
|
29
|
+
private send;
|
|
30
|
+
close(): void;
|
|
31
|
+
}
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
import { createLogger } from "../utils/logger/index.js";
|
|
2
|
+
import { init_logger } from "../utils/logger.js";
|
|
3
|
+
//#region src/observability/langfuse.ts
|
|
4
|
+
init_logger();
|
|
5
|
+
const log = createLogger("Observability:Langfuse");
|
|
6
|
+
function otlp(record) {
|
|
7
|
+
const attributes = Object.entries(record.attributes).map(([key, value]) => ({
|
|
8
|
+
key,
|
|
9
|
+
value: typeof value === "number" ? { doubleValue: value } : typeof value === "boolean" ? { boolValue: value } : { stringValue: value }
|
|
10
|
+
}));
|
|
11
|
+
return {
|
|
12
|
+
traceId: record.traceId,
|
|
13
|
+
spanId: record.spanId,
|
|
14
|
+
parentSpanId: record.parentSpanId,
|
|
15
|
+
name: record.name,
|
|
16
|
+
kind: 1,
|
|
17
|
+
startTimeUnixNano: String(BigInt(Math.floor(record.startedAt * 1e3)) * 1000n),
|
|
18
|
+
endTimeUnixNano: String(BigInt(Math.floor((record.endedAt ?? record.startedAt) * 1e3)) * 1000n),
|
|
19
|
+
attributes,
|
|
20
|
+
status: { code: record.status === "error" ? 2 : 1 }
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
/** Bounded OTLP/HTTP JSON exporter. No hidden SDK queue or durable replay. */
|
|
24
|
+
var LangfuseTarget = class {
|
|
25
|
+
queue = [];
|
|
26
|
+
bytes = 0;
|
|
27
|
+
timer;
|
|
28
|
+
sending;
|
|
29
|
+
controller;
|
|
30
|
+
closed = false;
|
|
31
|
+
lastWarning = 0;
|
|
32
|
+
dropped = 0;
|
|
33
|
+
lastSuccess;
|
|
34
|
+
lastFailure;
|
|
35
|
+
lastError;
|
|
36
|
+
constructor(baseUrl, publicKey, secretKey) {
|
|
37
|
+
this.baseUrl = baseUrl;
|
|
38
|
+
this.publicKey = publicKey;
|
|
39
|
+
this.secretKey = secretKey;
|
|
40
|
+
}
|
|
41
|
+
status() {
|
|
42
|
+
return {
|
|
43
|
+
state: this.lastError ? "degraded" : "ready",
|
|
44
|
+
pending: this.queue.length,
|
|
45
|
+
dropped: this.dropped,
|
|
46
|
+
lastSuccess: this.lastSuccess,
|
|
47
|
+
lastFailure: this.lastFailure,
|
|
48
|
+
lastError: this.lastError
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
enqueue(record) {
|
|
52
|
+
if (this.closed) return;
|
|
53
|
+
const bytes = Buffer.byteLength(JSON.stringify(record));
|
|
54
|
+
if (this.queue.length >= 2e3 || this.bytes + bytes > 8 * 1048576) {
|
|
55
|
+
this.dropped++;
|
|
56
|
+
this.lastError = "Export queue full";
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
this.queue.push({
|
|
60
|
+
record,
|
|
61
|
+
bytes
|
|
62
|
+
});
|
|
63
|
+
this.bytes += bytes;
|
|
64
|
+
if (this.queue.length >= 100) this.flush();
|
|
65
|
+
else if (!this.timer) {
|
|
66
|
+
this.timer = setTimeout(() => {
|
|
67
|
+
this.timer = void 0;
|
|
68
|
+
this.flush();
|
|
69
|
+
}, 5e3);
|
|
70
|
+
this.timer.unref();
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
async flush() {
|
|
74
|
+
if (this.sending) {
|
|
75
|
+
await this.sending;
|
|
76
|
+
if (this.queue.length && !this.closed) await this.flush();
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
if (this.timer) {
|
|
80
|
+
clearTimeout(this.timer);
|
|
81
|
+
this.timer = void 0;
|
|
82
|
+
}
|
|
83
|
+
if (this.closed || !this.queue.length) return;
|
|
84
|
+
const batch = this.queue.splice(0, 100);
|
|
85
|
+
this.bytes -= batch.reduce((n, row) => n + row.bytes, 0);
|
|
86
|
+
this.sending = this.send(batch.map((row) => row.record)).finally(() => {
|
|
87
|
+
this.sending = void 0;
|
|
88
|
+
});
|
|
89
|
+
await this.sending;
|
|
90
|
+
if (this.queue.length && !this.closed && !this.timer) {
|
|
91
|
+
this.timer = setTimeout(() => {
|
|
92
|
+
this.timer = void 0;
|
|
93
|
+
this.flush();
|
|
94
|
+
}, 5e3);
|
|
95
|
+
this.timer.unref();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async send(records) {
|
|
99
|
+
for (let attempt = 0; attempt < 3 && !this.closed; attempt++) {
|
|
100
|
+
this.controller = new AbortController();
|
|
101
|
+
const timeout = setTimeout(() => this.controller?.abort(), 5e3);
|
|
102
|
+
try {
|
|
103
|
+
const response = await fetch(this.baseUrl.replace(/\/$/, "") + "/api/public/otel/v1/traces", {
|
|
104
|
+
method: "POST",
|
|
105
|
+
redirect: "error",
|
|
106
|
+
signal: this.controller.signal,
|
|
107
|
+
headers: {
|
|
108
|
+
"Content-Type": "application/json",
|
|
109
|
+
Authorization: `Basic ${Buffer.from(`${this.publicKey}:${this.secretKey}`).toString("base64")}`,
|
|
110
|
+
"x-langfuse-ingestion-version": "4"
|
|
111
|
+
},
|
|
112
|
+
body: JSON.stringify({ resourceSpans: [{
|
|
113
|
+
resource: { attributes: [{
|
|
114
|
+
key: "service.name",
|
|
115
|
+
value: { stringValue: "xopc" }
|
|
116
|
+
}] },
|
|
117
|
+
scopeSpans: [{
|
|
118
|
+
scope: { name: "xopc.observability" },
|
|
119
|
+
spans: records.map(otlp)
|
|
120
|
+
}]
|
|
121
|
+
}] })
|
|
122
|
+
});
|
|
123
|
+
if (!response.ok) {
|
|
124
|
+
await response.body?.cancel();
|
|
125
|
+
throw new Error(`Langfuse returned HTTP ${response.status}`);
|
|
126
|
+
}
|
|
127
|
+
if (response.body) {
|
|
128
|
+
const reader = response.body.getReader();
|
|
129
|
+
const chunks = [];
|
|
130
|
+
let size = 0;
|
|
131
|
+
try {
|
|
132
|
+
while (true) {
|
|
133
|
+
const chunk = await reader.read();
|
|
134
|
+
if (chunk.done) break;
|
|
135
|
+
size += chunk.value.length;
|
|
136
|
+
if (size > 16384) {
|
|
137
|
+
await reader.cancel();
|
|
138
|
+
throw new Error("Oversized ingestion response");
|
|
139
|
+
}
|
|
140
|
+
chunks.push(chunk.value);
|
|
141
|
+
}
|
|
142
|
+
const text = Buffer.concat(chunks).toString("utf8");
|
|
143
|
+
if (text && Number(JSON.parse(text).partialSuccess?.rejectedSpans ?? 0) > 0) throw new Error("Ingestion rejected spans");
|
|
144
|
+
} finally {
|
|
145
|
+
reader.releaseLock();
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
this.lastSuccess = Date.now();
|
|
149
|
+
this.lastError = void 0;
|
|
150
|
+
return;
|
|
151
|
+
} catch (error) {
|
|
152
|
+
this.lastFailure = Date.now();
|
|
153
|
+
if (Date.now() - this.lastWarning > 6e4) {
|
|
154
|
+
this.lastWarning = Date.now();
|
|
155
|
+
log.warn({
|
|
156
|
+
phase: "langfuse_export",
|
|
157
|
+
attempt: attempt + 1
|
|
158
|
+
}, "Langfuse trace export failed; local recording continues");
|
|
159
|
+
}
|
|
160
|
+
this.lastError = error instanceof Error && /^Langfuse returned HTTP \d+$/.test(error.message) ? error.message : "Langfuse export failed";
|
|
161
|
+
} finally {
|
|
162
|
+
clearTimeout(timeout);
|
|
163
|
+
}
|
|
164
|
+
if (attempt < 2 && !this.closed) await new Promise((resolve) => {
|
|
165
|
+
setTimeout(resolve, 250 * 2 ** attempt).unref();
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
this.dropped += records.length;
|
|
169
|
+
}
|
|
170
|
+
close() {
|
|
171
|
+
this.closed = true;
|
|
172
|
+
this.controller?.abort();
|
|
173
|
+
if (this.timer) clearTimeout(this.timer);
|
|
174
|
+
this.dropped += this.queue.length;
|
|
175
|
+
this.queue = [];
|
|
176
|
+
this.bytes = 0;
|
|
177
|
+
}
|
|
178
|
+
};
|
|
179
|
+
//#endregion
|
|
180
|
+
export { LangfuseTarget };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { type Span, type Attributes } from '@opentelemetry/api';
|
|
2
|
+
import { type TracingConfig } from './config.js';
|
|
3
|
+
import { LocalTraceStore } from './store.js';
|
|
4
|
+
import type { TraceRecord } from './types.js';
|
|
5
|
+
export type TraceIdentity = {
|
|
6
|
+
conversationId?: string;
|
|
7
|
+
runId?: string;
|
|
8
|
+
agentId?: string;
|
|
9
|
+
transcriptId?: string;
|
|
10
|
+
};
|
|
11
|
+
export declare function localStore(): LocalTraceStore;
|
|
12
|
+
export declare function tracingConfig(): TracingConfig;
|
|
13
|
+
export declare function configureTracing(value?: unknown): Promise<void>;
|
|
14
|
+
export declare function tracingStatus(): Promise<{
|
|
15
|
+
local: any;
|
|
16
|
+
langfuse: {
|
|
17
|
+
state: string;
|
|
18
|
+
pending: number;
|
|
19
|
+
dropped: number;
|
|
20
|
+
lastSuccess: number;
|
|
21
|
+
lastFailure: number;
|
|
22
|
+
lastError: string;
|
|
23
|
+
} | {
|
|
24
|
+
state: string;
|
|
25
|
+
pending: number;
|
|
26
|
+
dropped: number;
|
|
27
|
+
};
|
|
28
|
+
active: number;
|
|
29
|
+
dropped: number;
|
|
30
|
+
}>;
|
|
31
|
+
export declare function traceIds(): {
|
|
32
|
+
traceId?: string;
|
|
33
|
+
spanId?: string;
|
|
34
|
+
};
|
|
35
|
+
export type Observation = {
|
|
36
|
+
span?: Span;
|
|
37
|
+
run: <T>(fn: () => T) => T;
|
|
38
|
+
finish: (status?: string, output?: unknown) => void;
|
|
39
|
+
input: (value: unknown) => void;
|
|
40
|
+
update: (values: Attributes) => void;
|
|
41
|
+
};
|
|
42
|
+
export declare function startObservation(name: string, type: TraceRecord['type'], identity?: TraceIdentity): Observation;
|
|
43
|
+
export declare function traceRun<T extends {
|
|
44
|
+
ok?: boolean;
|
|
45
|
+
stopReason?: string;
|
|
46
|
+
errorMessage?: string;
|
|
47
|
+
lastAssistantText?: string;
|
|
48
|
+
}>(name: string, identity: TraceIdentity, fn: () => Promise<T>, input?: unknown): Promise<T>;
|
|
49
|
+
export declare function traceTools<T extends {
|
|
50
|
+
name: string;
|
|
51
|
+
execute: (...args: any[]) => any;
|
|
52
|
+
}>(tools: T[]): T[];
|
|
53
|
+
export declare function flushTracing(): Promise<void>;
|
|
54
|
+
export declare function testLangfuse(): Promise<boolean>;
|
|
55
|
+
export declare function closeTracing(): Promise<void>;
|
|
56
|
+
export declare function traceOperation<T>(name: string, fn: () => Promise<T>, attributes?: Attributes): Promise<T>;
|
|
57
|
+
export declare function setTraceIdentity(identity: TraceIdentity): void;
|