@amplitude/ai 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/.cursor/skills/instrument-with-amplitude-ai/SKILL.md +50 -0
- package/AGENTS.md +79 -0
- package/LICENSE +21 -0
- package/README.md +2111 -0
- package/bin/amplitude-ai-completions.mjs +19 -0
- package/bin/amplitude-ai-doctor.mjs +26 -0
- package/bin/amplitude-ai-init.mjs +27 -0
- package/bin/amplitude-ai-instrument.mjs +52 -0
- package/bin/amplitude-ai-mcp.mjs +8 -0
- package/bin/amplitude-ai-register-catalog.mjs +156 -0
- package/bin/amplitude-ai-status.mjs +28 -0
- package/bin/amplitude-ai.mjs +43 -0
- package/bin/validate-catalog-constants.mjs +134 -0
- package/data/agent_event_catalog.json +1277 -0
- package/dist/_virtual/rolldown_runtime.js +31 -0
- package/dist/bound-agent.d.ts +56 -0
- package/dist/bound-agent.d.ts.map +1 -0
- package/dist/bound-agent.js +155 -0
- package/dist/bound-agent.js.map +1 -0
- package/dist/cli/doctor.d.ts +17 -0
- package/dist/cli/doctor.d.ts.map +1 -0
- package/dist/cli/doctor.js +89 -0
- package/dist/cli/doctor.js.map +1 -0
- package/dist/cli/init.d.ts +14 -0
- package/dist/cli/init.d.ts.map +1 -0
- package/dist/cli/init.js +40 -0
- package/dist/cli/init.js.map +1 -0
- package/dist/cli/providers.d.ts +32 -0
- package/dist/cli/providers.d.ts.map +1 -0
- package/dist/cli/providers.js +46 -0
- package/dist/cli/providers.js.map +1 -0
- package/dist/cli/status.d.ts +14 -0
- package/dist/cli/status.d.ts.map +1 -0
- package/dist/cli/status.js +38 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/client.d.ts +246 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +369 -0
- package/dist/client.js.map +1 -0
- package/dist/config.d.ts +60 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +57 -0
- package/dist/config.js.map +1 -0
- package/dist/context.d.ts +44 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +53 -0
- package/dist/context.js.map +1 -0
- package/dist/core/constants.d.ts +96 -0
- package/dist/core/constants.d.ts.map +1 -0
- package/dist/core/constants.js +105 -0
- package/dist/core/constants.js.map +1 -0
- package/dist/core/enrichments.d.ts +139 -0
- package/dist/core/enrichments.d.ts.map +1 -0
- package/dist/core/enrichments.js +177 -0
- package/dist/core/enrichments.js.map +1 -0
- package/dist/core/privacy.d.ts +53 -0
- package/dist/core/privacy.d.ts.map +1 -0
- package/dist/core/privacy.js +235 -0
- package/dist/core/privacy.js.map +1 -0
- package/dist/core/tracking.d.ts +242 -0
- package/dist/core/tracking.d.ts.map +1 -0
- package/dist/core/tracking.js +469 -0
- package/dist/core/tracking.js.map +1 -0
- package/dist/decorators.d.ts +109 -0
- package/dist/decorators.d.ts.map +1 -0
- package/dist/decorators.js +281 -0
- package/dist/decorators.js.map +1 -0
- package/dist/exceptions.d.ts +19 -0
- package/dist/exceptions.d.ts.map +1 -0
- package/dist/exceptions.js +35 -0
- package/dist/exceptions.js.map +1 -0
- package/dist/index.d.ts +37 -0
- package/dist/index.js +38 -0
- package/dist/integrations/anthropic-tools.d.ts +38 -0
- package/dist/integrations/anthropic-tools.d.ts.map +1 -0
- package/dist/integrations/anthropic-tools.js +175 -0
- package/dist/integrations/anthropic-tools.js.map +1 -0
- package/dist/integrations/crewai.d.ts +14 -0
- package/dist/integrations/crewai.d.ts.map +1 -0
- package/dist/integrations/crewai.js +19 -0
- package/dist/integrations/crewai.js.map +1 -0
- package/dist/integrations/langchain.d.ts +37 -0
- package/dist/integrations/langchain.d.ts.map +1 -0
- package/dist/integrations/langchain.js +189 -0
- package/dist/integrations/langchain.js.map +1 -0
- package/dist/integrations/llamaindex.d.ts +44 -0
- package/dist/integrations/llamaindex.d.ts.map +1 -0
- package/dist/integrations/llamaindex.js +144 -0
- package/dist/integrations/llamaindex.js.map +1 -0
- package/dist/integrations/openai-agents.d.ts +35 -0
- package/dist/integrations/openai-agents.d.ts.map +1 -0
- package/dist/integrations/openai-agents.js +248 -0
- package/dist/integrations/openai-agents.js.map +1 -0
- package/dist/integrations/opentelemetry.d.ts +40 -0
- package/dist/integrations/opentelemetry.d.ts.map +1 -0
- package/dist/integrations/opentelemetry.js +146 -0
- package/dist/integrations/opentelemetry.js.map +1 -0
- package/dist/internals.d.ts +5 -0
- package/dist/internals.js +5 -0
- package/dist/mcp/contract.d.ts +27 -0
- package/dist/mcp/contract.d.ts.map +1 -0
- package/dist/mcp/contract.js +25 -0
- package/dist/mcp/contract.js.map +1 -0
- package/dist/mcp/index.d.ts +4 -0
- package/dist/mcp/index.js +5 -0
- package/dist/mcp/patterns.d.ts +11 -0
- package/dist/mcp/patterns.d.ts.map +1 -0
- package/dist/mcp/patterns.js +38 -0
- package/dist/mcp/patterns.js.map +1 -0
- package/dist/mcp/server.d.ts +10 -0
- package/dist/mcp/server.d.ts.map +1 -0
- package/dist/mcp/server.js +241 -0
- package/dist/mcp/server.js.map +1 -0
- package/dist/mcp/validate-file.d.ts +20 -0
- package/dist/mcp/validate-file.d.ts.map +1 -0
- package/dist/mcp/validate-file.js +122 -0
- package/dist/mcp/validate-file.js.map +1 -0
- package/dist/middleware.d.ts +32 -0
- package/dist/middleware.d.ts.map +1 -0
- package/dist/middleware.js +67 -0
- package/dist/middleware.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js +54 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/helpers.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js +20 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/interfaces.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js +34 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/mcp-server.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js +225 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/experimental/tasks/server.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js +22 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/completable.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js +351 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/index.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js +634 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/mcp.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js +60 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/stdio.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js +122 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js +37 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-json-schema-compat.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js +858 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/protocol.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js +32 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/stdio.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js +74 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/shared/toolNameValidation.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js +1262 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/types.js.map +1 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js +83 -0
- package/dist/node_modules/.pnpm/@modelcontextprotocol_sdk@1.27.1_zod@3.25.76/node_modules/@modelcontextprotocol/sdk/dist/esm/validation/ajv-provider.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/formats.js +195 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/formats.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/index.js +44 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/limit.js +88 -0
- package/dist/node_modules/.pnpm/ajv-formats@3.0.1_ajv@8.18.0/node_modules/ajv-formats/dist/limit.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/ajv.js +107 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/ajv.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js +142 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/code.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/index.js +678 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/scope.js +145 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/codegen/scope.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/errors.js +106 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/errors.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/index.js +227 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/names.js +33 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/names.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/ref_error.js +22 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/ref_error.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/resolve.js +137 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/resolve.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/rules.js +65 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/rules.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/util.js +150 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/util.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/applicability.js +27 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/applicability.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/boolSchema.js +52 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/boolSchema.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/dataType.js +176 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/dataType.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/defaults.js +35 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/defaults.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/index.js +437 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/keyword.js +117 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/keyword.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/subschema.js +76 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/compile/validate/subschema.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/core.js +587 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/core.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.js +22 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/data.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/json-schema-draft-07.js +145 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/refs/json-schema-draft-07.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/equal.js +16 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/equal.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/ucs2length.js +29 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/ucs2length.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/uri.js +16 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/uri.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/validation_error.js +20 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/runtime/validation_error.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js +61 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/additionalItems.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js +101 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/additionalProperties.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/allOf.js +34 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/allOf.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/anyOf.js +21 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/anyOf.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/contains.js +94 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/contains.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/dependencies.js +91 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/dependencies.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/if.js +66 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/if.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/index.js +64 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/items.js +63 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/items.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/items2020.js +39 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/items2020.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/not.js +36 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/not.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/oneOf.js +56 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/oneOf.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js +66 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/patternProperties.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js +22 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/prefixItems.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/properties.js +57 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/properties.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js +46 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/propertyNames.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/thenElse.js +22 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/applicator/thenElse.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/code.js +134 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/code.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/id.js +19 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/id.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/index.js +27 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/ref.js +116 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/core/ref.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/discriminator/index.js +106 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/discriminator/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/discriminator/types.js +18 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/discriminator/types.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/draft7.js +31 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/draft7.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/format/format.js +97 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/format/format.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/format/index.js +15 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/format/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/metadata.js +27 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/metadata.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/const.js +32 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/const.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/enum.js +54 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/enum.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/index.js +53 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/index.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitItems.js +33 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitItems.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitLength.js +38 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitLength.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitNumber.js +52 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitNumber.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitProperties.js +33 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/limitProperties.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/multipleOf.js +32 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/multipleOf.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/pattern.js +43 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/pattern.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/required.js +77 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/required.js.map +1 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js +75 -0
- package/dist/node_modules/.pnpm/ajv@8.18.0/node_modules/ajv/dist/vocabularies/validation/uniqueItems.js.map +1 -0
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js +37 -0
- package/dist/node_modules/.pnpm/fast-deep-equal@3.1.3/node_modules/fast-deep-equal/index.js.map +1 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/index.js +240 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/index.js.map +1 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/schemes.js +194 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/schemes.js.map +1 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js +286 -0
- package/dist/node_modules/.pnpm/fast-uri@3.1.0/node_modules/fast-uri/lib/utils.js.map +1 -0
- package/dist/node_modules/.pnpm/json-schema-traverse@1.0.0/node_modules/json-schema-traverse/index.js +82 -0
- package/dist/node_modules/.pnpm/json-schema-traverse@1.0.0/node_modules/json-schema-traverse/index.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js +37 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Options.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Refs.js +30 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/Refs.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/errorMessages.js +16 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/errorMessages.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/getRelativePath.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parseDef.js +57 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parseDef.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/any.js +17 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/any.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/array.js +23 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/array.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js +36 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/bigint.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js +8 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/boolean.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/branded.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/catch.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/date.js +39 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/date.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/default.js +13 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/default.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js +11 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/effects.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js +11 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/enum.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js +47 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/intersection.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js +17 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/literal.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/map.js +38 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/map.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js +16 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nativeEnum.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/never.js +13 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/never.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/null.js +11 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/null.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js +46 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/nullable.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/number.js +37 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/number.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/object.js +64 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/object.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js +20 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/optional.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js +27 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/pipeline.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/promise.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/readonly.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/record.js +56 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/record.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/set.js +21 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/set.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/string.js +250 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/string.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js +38 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/tuple.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/undefined.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/union.js +64 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/union.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js +10 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/parsers/unknown.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/selectParser.js +78 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/selectParser.js.map +1 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js +70 -0
- package/dist/node_modules/.pnpm/zod-to-json-schema@3.25.1_zod@3.25.76/node_modules/zod-to-json-schema/dist/esm/zodToJsonSchema.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js +104 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/ZodError.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js +11 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/errors.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js +10 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/errorUtil.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js +110 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/parseUtil.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js +99 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/helpers/util.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js +84 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/locales/en.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js +3160 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v3/types.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/errors.js +23 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/errors.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/iso.js +38 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/iso.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/parse.js +12 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/parse.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/schemas.js +611 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/classic/schemas.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/api.js +437 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/api.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/checks.js +360 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/checks.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js +50 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/core.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/doc.js +34 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/doc.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/errors.js +71 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/errors.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/parse.js +74 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/parse.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/regexes.js +63 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/regexes.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/registries.js +50 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/registries.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/schemas.js +1098 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/schemas.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/to-json-schema.js +599 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/to-json-schema.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/util.js +267 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/util.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/versions.js +10 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/core/versions.js.map +1 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/mini/schemas.js +50 -0
- package/dist/node_modules/.pnpm/zod@3.25.76/node_modules/zod/v4/mini/schemas.js.map +1 -0
- package/dist/patching.d.ts +59 -0
- package/dist/patching.d.ts.map +1 -0
- package/dist/patching.js +891 -0
- package/dist/patching.js.map +1 -0
- package/dist/propagation.d.ts +8 -0
- package/dist/propagation.d.ts.map +1 -0
- package/dist/propagation.js +46 -0
- package/dist/propagation.js.map +1 -0
- package/dist/providers/anthropic.d.ts +44 -0
- package/dist/providers/anthropic.d.ts.map +1 -0
- package/dist/providers/anthropic.js +287 -0
- package/dist/providers/anthropic.js.map +1 -0
- package/dist/providers/azure-openai.d.ts +32 -0
- package/dist/providers/azure-openai.d.ts.map +1 -0
- package/dist/providers/azure-openai.js +39 -0
- package/dist/providers/azure-openai.js.map +1 -0
- package/dist/providers/base.d.ts +70 -0
- package/dist/providers/base.d.ts.map +1 -0
- package/dist/providers/base.js +141 -0
- package/dist/providers/base.js.map +1 -0
- package/dist/providers/bedrock.d.ts +41 -0
- package/dist/providers/bedrock.d.ts.map +1 -0
- package/dist/providers/bedrock.js +229 -0
- package/dist/providers/bedrock.js.map +1 -0
- package/dist/providers/gemini.d.ts +34 -0
- package/dist/providers/gemini.d.ts.map +1 -0
- package/dist/providers/gemini.js +225 -0
- package/dist/providers/gemini.js.map +1 -0
- package/dist/providers/mistral.d.ts +32 -0
- package/dist/providers/mistral.d.ts.map +1 -0
- package/dist/providers/mistral.js +218 -0
- package/dist/providers/mistral.js.map +1 -0
- package/dist/providers/openai.d.ts +61 -0
- package/dist/providers/openai.d.ts.map +1 -0
- package/dist/providers/openai.js +530 -0
- package/dist/providers/openai.js.map +1 -0
- package/dist/register.d.ts +1 -0
- package/dist/register.js +46 -0
- package/dist/register.js.map +1 -0
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +144 -0
- package/dist/session.js.map +1 -0
- package/dist/tenant.d.ts +30 -0
- package/dist/tenant.d.ts.map +1 -0
- package/dist/tenant.js +23 -0
- package/dist/tenant.js.map +1 -0
- package/dist/testing.d.ts +46 -0
- package/dist/testing.d.ts.map +1 -0
- package/dist/testing.js +120 -0
- package/dist/testing.js.map +1 -0
- package/dist/types.d.ts +384 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/costs.d.ts +27 -0
- package/dist/utils/costs.d.ts.map +1 -0
- package/dist/utils/costs.js +63 -0
- package/dist/utils/costs.js.map +1 -0
- package/dist/utils/debug.d.ts +6 -0
- package/dist/utils/debug.d.ts.map +1 -0
- package/dist/utils/debug.js +73 -0
- package/dist/utils/debug.js.map +1 -0
- package/dist/utils/logger.d.ts +11 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +18 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/model-tiers.d.ts +8 -0
- package/dist/utils/model-tiers.d.ts.map +1 -0
- package/dist/utils/model-tiers.js +76 -0
- package/dist/utils/model-tiers.js.map +1 -0
- package/dist/utils/providers.d.ts +5 -0
- package/dist/utils/providers.d.ts.map +1 -0
- package/dist/utils/providers.js +20 -0
- package/dist/utils/providers.js.map +1 -0
- package/dist/utils/resolve-module.d.ts +5 -0
- package/dist/utils/resolve-module.d.ts.map +1 -0
- package/dist/utils/resolve-module.js +29 -0
- package/dist/utils/resolve-module.js.map +1 -0
- package/dist/utils/streaming.d.ts +55 -0
- package/dist/utils/streaming.d.ts.map +1 -0
- package/dist/utils/streaming.js +72 -0
- package/dist/utils/streaming.js.map +1 -0
- package/dist/utils/tokens.d.ts +16 -0
- package/dist/utils/tokens.d.ts.map +1 -0
- package/dist/utils/tokens.js +80 -0
- package/dist/utils/tokens.js.map +1 -0
- package/dist/wrappers.d.ts +37 -0
- package/dist/wrappers.d.ts.map +1 -0
- package/dist/wrappers.js +61 -0
- package/dist/wrappers.js.map +1 -0
- package/llms-full.txt +127 -0
- package/llms.txt +28 -0
- package/mcp.schema.json +18 -0
- package/package.json +189 -0
package/README.md
ADDED
|
@@ -0,0 +1,2111 @@
|
|
|
1
|
+
# @amplitude/ai
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/@amplitude/ai)
|
|
4
|
+
[](https://github.com/amplitude/Amplitude-AI-Node/actions/workflows/test.yml)
|
|
5
|
+
[](https://opensource.org/licenses/MIT)
|
|
6
|
+
|
|
7
|
+
Agent analytics for [Amplitude](https://amplitude.com). Track every LLM call as events in your Amplitude project, then build funnels, cohorts, and retention charts across AI and product behavior.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @amplitude/ai @amplitude/analytics-node
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
```typescript
|
|
14
|
+
import { AmplitudeAI, patch } from '@amplitude/ai';
|
|
15
|
+
// Your existing code — unchanged
|
|
16
|
+
import OpenAI from 'openai';
|
|
17
|
+
|
|
18
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
19
|
+
patch({ amplitudeAI: ai });
|
|
20
|
+
|
|
21
|
+
const response = await new OpenAI().chat.completions.create({
|
|
22
|
+
model: 'gpt-4o',
|
|
23
|
+
messages: [{ role: 'user', content: 'What is retention?' }],
|
|
24
|
+
});
|
|
25
|
+
// [Agent] User Message + [Agent] AI Response now in your Amplitude project
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
One call auto-detects and patches every installed provider (OpenAI, Anthropic, Azure OpenAI, Gemini, Mistral, Bedrock). Want more control? See [Choose Your Integration Tier](#choose-your-integration-tier) below.
|
|
29
|
+
|
|
30
|
+
| Property | Value |
|
|
31
|
+
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
32
|
+
| Name | @amplitude/ai |
|
|
33
|
+
| Version | 0.1.0 |
|
|
34
|
+
| Runtime | Node.js |
|
|
35
|
+
| Peer dependency | @amplitude/analytics-node >= 1.3.0 |
|
|
36
|
+
| Optional peers | openai, @anthropic-ai/sdk, @google/generative-ai, @mistralai/mistralai, @aws-sdk/client-bedrock-runtime, @pydantic/genai-prices (cost), tiktoken or js-tiktoken (token counting) |
|
|
37
|
+
|
|
38
|
+
## Table of Contents
|
|
39
|
+
|
|
40
|
+
- [Installation](#installation)
|
|
41
|
+
- [Quick Start](#quick-start)
|
|
42
|
+
- [Choose Your Integration Tier](#choose-your-integration-tier)
|
|
43
|
+
- [Support matrix](#support-matrix)
|
|
44
|
+
- [Parity and runtime limitations](#parity-and-runtime-limitations)
|
|
45
|
+
- [Core Concepts](#core-concepts)
|
|
46
|
+
- [Configuration](#configuration)
|
|
47
|
+
- [Privacy & Content Control](#privacy--content-control)
|
|
48
|
+
- [Cache-Aware Cost Tracking](#cache-aware-cost-tracking)
|
|
49
|
+
- [Model Tier Classification](#model-tier-classification)
|
|
50
|
+
- [Provider Wrappers](#provider-wrappers)
|
|
51
|
+
- [Streaming Tracking](#streaming-tracking)
|
|
52
|
+
- [Attachment Tracking](#attachment-tracking)
|
|
53
|
+
- [tool() and observe() HOFs](#tool-and-observe-hofs)
|
|
54
|
+
- [Scoring Patterns](#scoring-patterns)
|
|
55
|
+
- [Enrichments](#enrichments)
|
|
56
|
+
- [Debug and Dry-Run Modes](#debug-and-dry-run-modes)
|
|
57
|
+
- [Patching (Zero-Code Instrumentation)](#patching-zero-code-instrumentation)
|
|
58
|
+
- [Auto-Instrumentation CLI](#auto-instrumentation-cli)
|
|
59
|
+
- [Integrations](#integrations)
|
|
60
|
+
- [Integration Patterns](#integration-patterns)
|
|
61
|
+
- [Serverless Environments](#serverless-environments)
|
|
62
|
+
- [Error Handling and Reliability](#error-handling-and-reliability)
|
|
63
|
+
- [Testing](#testing)
|
|
64
|
+
- [Troubleshooting](#troubleshooting)
|
|
65
|
+
- [Context Propagation](#context-propagation)
|
|
66
|
+
- [Middleware](#middleware)
|
|
67
|
+
- [Bulk Conversation Import](#bulk-conversation-import)
|
|
68
|
+
- [Event Schema](#event-schema)
|
|
69
|
+
- [Event Property Reference](#event-property-reference)
|
|
70
|
+
- [Event JSON Examples](#event-json-examples)
|
|
71
|
+
- [Sending Events Without the SDK](#sending-events-without-the-sdk)
|
|
72
|
+
- [Register Event Schema in Your Data Catalog](#register-event-schema-in-your-data-catalog)
|
|
73
|
+
- [Utilities and Type Exports](#utilities-and-type-exports)
|
|
74
|
+
- [Constants](#constants)
|
|
75
|
+
- [API Reference](#api-reference)
|
|
76
|
+
- [For AI Coding Agents](#for-ai-coding-agents)
|
|
77
|
+
- [For Python SDK Migrators](#for-python-sdk-migrators)
|
|
78
|
+
- [Need Help?](#need-help)
|
|
79
|
+
- [Contributing](#contributing)
|
|
80
|
+
- [License](#license)
|
|
81
|
+
|
|
82
|
+
## Installation
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
npm install @amplitude/ai @amplitude/analytics-node
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Install provider SDKs based on what you use (for example: `openai`, `@anthropic-ai/sdk`, `@google/generative-ai`, `@mistralai/mistralai`, `@aws-sdk/client-bedrock-runtime`).
|
|
89
|
+
|
|
90
|
+
## Quick Start
|
|
91
|
+
|
|
92
|
+
### Quickstart (5 minutes)
|
|
93
|
+
|
|
94
|
+
1. **Install:** `npm install @amplitude/ai @amplitude/analytics-node`
|
|
95
|
+
2. **Get your API key:** In Amplitude, go to **Settings > Projects** and copy the API key.
|
|
96
|
+
3. **Add two lines** to the top of your app (see the hero example above).
|
|
97
|
+
4. **Run your app.** Any OpenAI, Anthropic, Gemini, or Mistral call is now instrumented automatically.
|
|
98
|
+
5. **Open Amplitude > Events.** You should see `[Agent] AI Response` within 30 seconds.
|
|
99
|
+
|
|
100
|
+
To verify locally before checking Amplitude, add `debug: true`:
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { AIConfig, AmplitudeAI, patch } from '@amplitude/ai';
|
|
104
|
+
|
|
105
|
+
const ai = new AmplitudeAI({
|
|
106
|
+
apiKey: process.env.AMPLITUDE_AI_API_KEY!,
|
|
107
|
+
config: new AIConfig({ debug: true }),
|
|
108
|
+
});
|
|
109
|
+
patch({ amplitudeAI: ai });
|
|
110
|
+
// Prints: [amplitude-ai] [Agent] AI Response | model=gpt-4o | tokens=847 | cost=$0.0042 | latency=1,203ms
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
### Full-control quick start
|
|
114
|
+
|
|
115
|
+
For maximum flexibility, use `BoundAgent` + `Session` with explicit tracking calls:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
import { AmplitudeAI } from '@amplitude/ai';
|
|
119
|
+
|
|
120
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
121
|
+
|
|
122
|
+
const agent = ai.agent('my-agent', { userId: 'user-123' });
|
|
123
|
+
const session = agent.session();
|
|
124
|
+
|
|
125
|
+
await session.run(async (s) => {
|
|
126
|
+
s.trackUserMessage('What is the capital of France?');
|
|
127
|
+
|
|
128
|
+
const response = await callLLM('What is the capital of France?');
|
|
129
|
+
|
|
130
|
+
s.trackAiMessage(
|
|
131
|
+
response.content,
|
|
132
|
+
response.model,
|
|
133
|
+
'openai',
|
|
134
|
+
response.latencyMs,
|
|
135
|
+
{
|
|
136
|
+
inputTokens: response.inputTokens,
|
|
137
|
+
outputTokens: response.outputTokens,
|
|
138
|
+
},
|
|
139
|
+
);
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### What the quick setup gives you
|
|
144
|
+
|
|
145
|
+
The zero-code / CLI setup gives you cost, latency, token counts, and error tracking immediately. But cohorts, retention analysis, funnels, and server-side enrichment all require `userId` and `sessionId`. Without them you have aggregate monitoring — not per-user analytics.
|
|
146
|
+
|
|
147
|
+
| Setup | What you get |
|
|
148
|
+
| -------------------- | ------------------------------------------------- |
|
|
149
|
+
| CLI / zero-code only | Cost, latency, tokens, errors — aggregate only |
|
|
150
|
+
| + `userId` | Per-user funnels, cohorts, retention |
|
|
151
|
+
| + `sessionId` | Session grouping, enrichment, behavioral patterns |
|
|
152
|
+
|
|
153
|
+
Adding `userId` is one option per call. Adding session context is `session.run()`. See [Session](#session) and [Choose Your Integration Tier](#choose-your-integration-tier).
|
|
154
|
+
|
|
155
|
+
## Choose Your Integration Tier
|
|
156
|
+
|
|
157
|
+
| Tier | Code Changes | What You Get |
|
|
158
|
+
| --------------------- | ----------------------------------------------------- | ------------------------------------------------- |
|
|
159
|
+
| **Zero-code (patch)** | 2 lines | Auto-tracks supported provider calls via patching |
|
|
160
|
+
| **Wrap** | `wrap(client, ai)` or `new OpenAI({ amplitude: ai })` | Auto-tracking + full control of options per call |
|
|
161
|
+
| **Full control** | Call `trackUserMessage`/`trackAiMessage` directly | Maximum flexibility, works with any LLM provider |
|
|
162
|
+
|
|
163
|
+
### Support matrix
|
|
164
|
+
|
|
165
|
+
- Fully supported in Node.js: OpenAI chat completions, OpenAI Responses API, Azure OpenAI chat completions, Anthropic messages, Gemini, Mistral, Bedrock, LangChain, OpenTelemetry, LlamaIndex.
|
|
166
|
+
- Partial support: zero-code `patch()` is best-effort by installed SDK and provider surface; OpenAI Agents tracing depends on incoming span payload shape from the host SDK.
|
|
167
|
+
- Not currently supported in Node.js:
|
|
168
|
+
- `AmplitudeCrewAIHooks` is Python-only and throws in Node.js.
|
|
169
|
+
|
|
170
|
+
### Parity and runtime limitations
|
|
171
|
+
|
|
172
|
+
This section is the source of truth for behavior that is intentionally different from Python due to runtime constraints:
|
|
173
|
+
|
|
174
|
+
- `AmplitudeCrewAIHooks` is unsupported in Node.js (CrewAI is Python-only).
|
|
175
|
+
- `tool()` does not auto-generate JSON Schema from runtime type hints; pass `inputSchema` explicitly.
|
|
176
|
+
- Tool timeout behavior is async `Promise.race` based and cannot preempt synchronous CPU-bound code.
|
|
177
|
+
- Auto-instrument bootstrap differs by runtime (`node --import` in Node vs `sitecustomize` in Python).
|
|
178
|
+
- Request middleware differs by runtime (Express-compatible in Node vs ASGI middleware in Python).
|
|
179
|
+
|
|
180
|
+
### Zero-code (recommended for getting started)
|
|
181
|
+
|
|
182
|
+
Add two lines at application startup and LLM calls through supported patched providers are tracked automatically:
|
|
183
|
+
|
|
184
|
+
```typescript
|
|
185
|
+
import { AmplitudeAI, patch } from '@amplitude/ai';
|
|
186
|
+
// OpenAI/Azure OpenAI chat completions (+ parse), OpenAI Responses, Anthropic, Gemini, Mistral,
|
|
187
|
+
// and Bedrock Converse calls are tracked when patching succeeds.
|
|
188
|
+
// No changes to your existing code needed.
|
|
189
|
+
import OpenAI from 'openai';
|
|
190
|
+
|
|
191
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
192
|
+
patch({ amplitudeAI: ai });
|
|
193
|
+
|
|
194
|
+
const openai = new OpenAI();
|
|
195
|
+
|
|
196
|
+
const response = await openai.chat.completions.create({
|
|
197
|
+
model: 'gpt-4o',
|
|
198
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
199
|
+
});
|
|
200
|
+
// ^ automatically tracked as [Agent] AI Response
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
> **Warning:** Patched calls that fire outside an active session context are **silently dropped** — no event is emitted and no error is thrown. If you instrument with `patch()` but see no events, this is the most likely cause. Wrap your LLM calls in `session.run()`, use the Express middleware, or pass context explicitly. See [Session](#session) and [Middleware](#middleware).
|
|
204
|
+
|
|
205
|
+
Or use the CLI to auto-patch at process start without touching application code:
|
|
206
|
+
|
|
207
|
+
```bash
|
|
208
|
+
AMPLITUDE_AI_API_KEY=xxx AMPLITUDE_AI_AUTO_PATCH=true amplitude-ai-instrument node app.js
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
### Wrap (recommended for production)
|
|
212
|
+
|
|
213
|
+
Replace the provider constructor with the Amplitude-instrumented version for automatic tracking with full control over options per call:
|
|
214
|
+
|
|
215
|
+
```typescript
|
|
216
|
+
import { AmplitudeAI, OpenAI } from '@amplitude/ai';
|
|
217
|
+
|
|
218
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
219
|
+
const openai = new OpenAI({
|
|
220
|
+
amplitude: ai,
|
|
221
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const agent = ai.agent('my-agent', { userId: 'user-123' });
|
|
225
|
+
const session = agent.session();
|
|
226
|
+
|
|
227
|
+
await session.run(async () => {
|
|
228
|
+
const response = await openai.chat.completions.create({
|
|
229
|
+
model: 'gpt-4o',
|
|
230
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
231
|
+
});
|
|
232
|
+
// AI response tracked automatically via wrapper
|
|
233
|
+
|
|
234
|
+
const responseV2 = await openai.responses.create({
|
|
235
|
+
model: 'gpt-4.1',
|
|
236
|
+
instructions: 'You are concise.',
|
|
237
|
+
input: [{ role: 'user', content: 'Summarize this in one sentence.' }],
|
|
238
|
+
});
|
|
239
|
+
// OpenAI Responses API is also tracked automatically
|
|
240
|
+
});
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
Or wrap an existing client instance (supports OpenAI, Azure OpenAI, and Anthropic):
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
import { wrap } from '@amplitude/ai';
|
|
247
|
+
import OpenAI from 'openai';
|
|
248
|
+
|
|
249
|
+
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
250
|
+
const instrumented = wrap(client, ai);
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
All provider constructors and `wrap()` accept either an `AmplitudeAI` instance or a raw Amplitude client — both work:
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
new OpenAI({ amplitude: ai }); // AmplitudeAI instance
|
|
257
|
+
new OpenAI({ amplitude: ai.amplitude }); // raw Amplitude client
|
|
258
|
+
wrap(client, ai); // AmplitudeAI instance
|
|
259
|
+
wrap(client, ai.amplitude); // raw Amplitude client
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
> **Note:** `wrap()` only supports OpenAI, Azure OpenAI, and Anthropic clients. For Gemini, Mistral, and Bedrock, use the SDK's provider classes directly (e.g., `new Gemini({ amplitude: ai })`).
|
|
263
|
+
|
|
264
|
+
### Full control
|
|
265
|
+
|
|
266
|
+
Call tracking methods directly for maximum flexibility. Works with any LLM provider, including custom or self-hosted models:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
import { AmplitudeAI } from '@amplitude/ai';
|
|
270
|
+
|
|
271
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
272
|
+
const agent = ai.agent('my-agent', { userId: 'user-123' });
|
|
273
|
+
const session = agent.session({ userId: 'user-123' });
|
|
274
|
+
|
|
275
|
+
await session.run(async (s) => {
|
|
276
|
+
s.trackUserMessage('Summarize this document');
|
|
277
|
+
|
|
278
|
+
const start = performance.now();
|
|
279
|
+
const response = await myCustomLLM.generate('Summarize this document');
|
|
280
|
+
const latencyMs = performance.now() - start;
|
|
281
|
+
|
|
282
|
+
s.trackAiMessage(response.text, 'my-model-v2', 'custom', latencyMs, {
|
|
283
|
+
inputTokens: response.usage.input,
|
|
284
|
+
outputTokens: response.usage.output,
|
|
285
|
+
});
|
|
286
|
+
});
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Core Concepts
|
|
290
|
+
|
|
291
|
+
### AmplitudeAI
|
|
292
|
+
|
|
293
|
+
Main client that wraps Amplitude `analytics-node`. Create it with an API key or an existing Amplitude instance:
|
|
294
|
+
|
|
295
|
+
```typescript
|
|
296
|
+
const ai = new AmplitudeAI({ apiKey: 'YOUR_API_KEY' });
|
|
297
|
+
// Or with existing client:
|
|
298
|
+
const ai = new AmplitudeAI({ amplitude: existingAmplitudeClient });
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### BoundAgent
|
|
302
|
+
|
|
303
|
+
Agent with pre-bound defaults (`agentId`, `userId`, `env`, etc.). Use `agent()` to create:
|
|
304
|
+
|
|
305
|
+
```typescript
|
|
306
|
+
const agent = ai.agent('support-bot', {
|
|
307
|
+
userId: 'user-123',
|
|
308
|
+
env: 'production',
|
|
309
|
+
customerOrgId: 'org-456',
|
|
310
|
+
});
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
Child agents inherit context from their parent and automatically set `parentAgentId`:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const orchestrator = ai.agent('orchestrator', { userId: 'user-123' });
|
|
317
|
+
const researcher = orchestrator.child('researcher');
|
|
318
|
+
const writer = orchestrator.child('writer');
|
|
319
|
+
// researcher.parentAgentId === 'orchestrator'
|
|
320
|
+
```
|
|
321
|
+
|
|
322
|
+
### TenantHandle
|
|
323
|
+
|
|
324
|
+
Multi-tenant helper that pre-binds `customerOrgId` for all agents created from it:
|
|
325
|
+
|
|
326
|
+
```typescript
|
|
327
|
+
const tenant = ai.tenant('org-456', { env: 'production' });
|
|
328
|
+
const agent = tenant.agent('support-bot', { userId: 'user-123' });
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Session
|
|
332
|
+
|
|
333
|
+
Async context manager using `AsyncLocalStorage`. Use `session.run()` to execute a callback within session context; session end is tracked automatically on exit:
|
|
334
|
+
|
|
335
|
+
```typescript
|
|
336
|
+
const session = agent.session({ userId: 'user-123' });
|
|
337
|
+
await session.run(async (s) => {
|
|
338
|
+
s.trackUserMessage('Hello');
|
|
339
|
+
s.trackAiMessage(response.content, 'gpt-4', 'openai', latencyMs);
|
|
340
|
+
});
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
Start a new trace within an ongoing session to group related operations:
|
|
344
|
+
|
|
345
|
+
```typescript
|
|
346
|
+
await session.run(async (s) => {
|
|
347
|
+
const traceId = s.newTrace();
|
|
348
|
+
// All subsequent tracking calls inherit this traceId
|
|
349
|
+
s.trackUserMessage('Follow-up question');
|
|
350
|
+
s.trackAiMessage(response.content, 'gpt-4o', 'openai', latencyMs);
|
|
351
|
+
});
|
|
352
|
+
```
|
|
353
|
+
|
|
354
|
+
### tool()
|
|
355
|
+
|
|
356
|
+
Higher-order function wrapping functions to auto-track as `[Agent] Tool Call` events:
|
|
357
|
+
|
|
358
|
+
```typescript
|
|
359
|
+
import { tool } from '@amplitude/ai';
|
|
360
|
+
|
|
361
|
+
const searchDb = tool(
|
|
362
|
+
async (query: { q: string }) => {
|
|
363
|
+
return await db.search(query.q);
|
|
364
|
+
},
|
|
365
|
+
{
|
|
366
|
+
name: 'search_db',
|
|
367
|
+
inputSchema: { type: 'object', properties: { q: { type: 'string' } } },
|
|
368
|
+
},
|
|
369
|
+
);
|
|
370
|
+
```
|
|
371
|
+
|
|
372
|
+
**Note on `inputSchema`**: Unlike the Python SDK which accepts a Pydantic model class and extracts the JSON Schema automatically, the TypeScript SDK accepts a raw JSON Schema object. For type-safe schema generation, consider using [Zod](https://zod.dev) with `zod-to-json-schema`:
|
|
373
|
+
|
|
374
|
+
```typescript
|
|
375
|
+
import { z } from 'zod';
|
|
376
|
+
import { zodToJsonSchema } from 'zod-to-json-schema';
|
|
377
|
+
|
|
378
|
+
const QuerySchema = z.object({ q: z.string(), limit: z.number().optional() });
|
|
379
|
+
const searchDb = tool(mySearchFn, {
|
|
380
|
+
name: 'search_db',
|
|
381
|
+
inputSchema: zodToJsonSchema(QuerySchema),
|
|
382
|
+
});
|
|
383
|
+
```
|
|
384
|
+
|
|
385
|
+
### observe()
|
|
386
|
+
|
|
387
|
+
Higher-order function wrapping functions to auto-track as `[Agent] Span` events:
|
|
388
|
+
|
|
389
|
+
```typescript
|
|
390
|
+
import { observe } from '@amplitude/ai';
|
|
391
|
+
|
|
392
|
+
const processRequest = observe(
|
|
393
|
+
async (input: Request) => {
|
|
394
|
+
return await handleRequest(input);
|
|
395
|
+
},
|
|
396
|
+
{ name: 'process_request' },
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Configuration
|
|
401
|
+
|
|
402
|
+
```typescript
|
|
403
|
+
import { AIConfig, AmplitudeAI, ContentMode } from '@amplitude/ai';
|
|
404
|
+
|
|
405
|
+
const config = new AIConfig({
|
|
406
|
+
contentMode: ContentMode.FULL, // FULL | METADATA_ONLY | CUSTOMER_ENRICHED — both ContentMode.FULL and 'full' work
|
|
407
|
+
redactPii: true,
|
|
408
|
+
customRedactionPatterns: ['sensitive-\\d+'],
|
|
409
|
+
debug: false,
|
|
410
|
+
dryRun: false,
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
const ai = new AmplitudeAI({ apiKey: 'YOUR_API_KEY', config });
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
| Option | Description |
|
|
417
|
+
| ------------------------- | ----------------------------------------------------------------------------------------------------------- |
|
|
418
|
+
| `contentMode` | `'full'` (default), `'metadata_only'`, or `'customer_enriched'`. Both `ContentMode.FULL` and `'full'` work. |
|
|
419
|
+
| `redactPii` | Redact email, phone, SSN, credit card patterns |
|
|
420
|
+
| `customRedactionPatterns` | Additional regex patterns for redaction |
|
|
421
|
+
| `debug` | Log events to stderr |
|
|
422
|
+
| `dryRun` | Log without sending to Amplitude |
|
|
423
|
+
| `validate` | Enable strict validation of required fields |
|
|
424
|
+
| `onEventCallback` | Callback invoked after every tracked event `(event, statusCode, message) => void` |
|
|
425
|
+
| `propagateContext` | Enable cross-service context propagation |
|
|
426
|
+
|
|
427
|
+
## Privacy & Content Control
|
|
428
|
+
|
|
429
|
+
Three content modes control what data is sent to Amplitude:
|
|
430
|
+
|
|
431
|
+
| Mode | Message Content | Token/Cost/Latency | Session Grouping | Server Enrichments |
|
|
432
|
+
| ------------------- | ------------------------- | ------------------ | ---------------- | ------------------ |
|
|
433
|
+
| `FULL` | Sent (with PII redaction) | Yes | Yes | Yes (auto) |
|
|
434
|
+
| `METADATA_ONLY` | Not sent | Yes | Yes | No |
|
|
435
|
+
| `CUSTOMER_ENRICHED` | Not sent | Yes | Yes | Yes (you provide) |
|
|
436
|
+
|
|
437
|
+
### FULL mode (default)
|
|
438
|
+
|
|
439
|
+
Message content is captured and sent to Amplitude. When you opt in with `redactPii: true`, built-in PII redaction patterns scrub emails, phone numbers, SSNs, credit card numbers, and base64 image data before the event leaves your process:
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
const config = new AIConfig({
|
|
443
|
+
contentMode: ContentMode.FULL,
|
|
444
|
+
redactPii: true,
|
|
445
|
+
});
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
With `redactPii: true`, a message like `"Contact me at john@example.com or 555-123-4567"` is sanitized to `"Contact me at [email] or [phone]"` before being sent.
|
|
449
|
+
|
|
450
|
+
Built-in phone and SSN detection are currently tuned for common US formats. If you need broader international coverage, add explicit `customRedactionPatterns` for your locales.
|
|
451
|
+
|
|
452
|
+
Add custom redaction patterns for domain-specific PII:
|
|
453
|
+
|
|
454
|
+
```typescript
|
|
455
|
+
const config = new AIConfig({
|
|
456
|
+
contentMode: ContentMode.FULL,
|
|
457
|
+
redactPii: true,
|
|
458
|
+
customRedactionPatterns: ['ACCT-\\d{6,}', 'internal-key-[a-f0-9]+'],
|
|
459
|
+
});
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
Custom redaction patterns are your responsibility: avoid expensive or catastrophic regexes in performance-sensitive paths.
|
|
463
|
+
|
|
464
|
+
Message content is stored at full length with no truncation or size limits. The `$llm_message` property is whitelisted server-side, and the Node SDK does not apply per-property string truncation.
|
|
465
|
+
|
|
466
|
+
### METADATA_ONLY mode
|
|
467
|
+
|
|
468
|
+
No message content is sent. You still get token counts, cost, latency, model name, and session grouping — everything needed for cost analytics and performance monitoring:
|
|
469
|
+
|
|
470
|
+
```typescript
|
|
471
|
+
const config = new AIConfig({
|
|
472
|
+
contentMode: ContentMode.METADATA_ONLY,
|
|
473
|
+
});
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Use this when you cannot send user content to a third-party analytics service (e.g., regulated industries, sensitive data).
|
|
477
|
+
|
|
478
|
+
### CUSTOMER_ENRICHED mode
|
|
479
|
+
|
|
480
|
+
Like `METADATA_ONLY` (no content sent), but designed for workflows where you enrich sessions with your own classifications, quality scores, and topic labels via the `SessionEnrichments` API:
|
|
481
|
+
|
|
482
|
+
```typescript
|
|
483
|
+
const config = new AIConfig({
|
|
484
|
+
contentMode: ContentMode.CUSTOMER_ENRICHED,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
// Later, after running your own classification pipeline:
|
|
488
|
+
const enrichments = new SessionEnrichments({
|
|
489
|
+
qualityScore: 0.85,
|
|
490
|
+
overallOutcome: 'resolved',
|
|
491
|
+
});
|
|
492
|
+
session.setEnrichments(enrichments);
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
### PrivacyConfig (advanced)
|
|
496
|
+
|
|
497
|
+
`PrivacyConfig` is derived from `AIConfig` via `config.toPrivacyConfig()`. For advanced use, create directly:
|
|
498
|
+
|
|
499
|
+
```typescript
|
|
500
|
+
import { PrivacyConfig } from '@amplitude/ai';
|
|
501
|
+
|
|
502
|
+
const privacy = new PrivacyConfig({
|
|
503
|
+
privacyMode: true,
|
|
504
|
+
redactPii: true,
|
|
505
|
+
customRedactionPatterns: ['sensitive-\\d+'],
|
|
506
|
+
});
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
### When to use which mode
|
|
510
|
+
|
|
511
|
+
- **FULL**: You want to see actual conversation content in Amplitude, debug individual sessions, and leverage server-side enrichment pipelines. Best for development, internal tools, and applications where data sharing agreements permit it.
|
|
512
|
+
- **METADATA_ONLY**: You want cost/performance analytics without exposing any message content. Best for regulated environments (healthcare, finance) or when content contains proprietary data.
|
|
513
|
+
- **CUSTOMER_ENRICHED**: You want the privacy of METADATA_ONLY but also want structured analytics (topic classification, quality scores) that you compute on your own infrastructure before sending to Amplitude.
|
|
514
|
+
|
|
515
|
+
## Cache-Aware Cost Tracking
|
|
516
|
+
|
|
517
|
+
When using provider prompt caching (Anthropic's cache, OpenAI's cached completions, etc.), pass cache token breakdowns for accurate cost calculation:
|
|
518
|
+
|
|
519
|
+
```typescript
|
|
520
|
+
s.trackAiMessage(
|
|
521
|
+
response.content,
|
|
522
|
+
'claude-3.5-sonnet',
|
|
523
|
+
'anthropic',
|
|
524
|
+
latencyMs,
|
|
525
|
+
{
|
|
526
|
+
inputTokens: response.usage.input_tokens,
|
|
527
|
+
outputTokens: response.usage.output_tokens,
|
|
528
|
+
cacheReadTokens: response.usage.cache_read_input_tokens,
|
|
529
|
+
cacheCreationTokens: response.usage.cache_creation_input_tokens,
|
|
530
|
+
},
|
|
531
|
+
);
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
Without cache breakdowns, cost calculation treats all input tokens at the standard rate. With caching enabled, cache-read tokens are typically 10x cheaper than standard input tokens and cache-creation tokens are ~25% more expensive. Naive cost calculation without this breakdown can overestimate costs by 2-5x for cache-heavy workloads.
|
|
535
|
+
|
|
536
|
+
The SDK tracks four token categories:
|
|
537
|
+
|
|
538
|
+
- `[Agent] Input Tokens` — standard (non-cached) input tokens
|
|
539
|
+
- `[Agent] Output Tokens` — generated output tokens
|
|
540
|
+
- `[Agent] Cache Read Tokens` — tokens read from provider cache (cheap)
|
|
541
|
+
- `[Agent] Cache Creation Tokens` — tokens written to provider cache (slightly expensive)
|
|
542
|
+
|
|
543
|
+
Cost is auto-calculated when token counts are provided and the `@pydantic/genai-prices` package is installed. When genai-prices is not available, `calculateCost()` returns `0` (never `null`). You can also pass `totalCostUsd` directly if you compute cost yourself:
|
|
544
|
+
|
|
545
|
+
```typescript
|
|
546
|
+
s.trackAiMessage(response.content, 'gpt-4o', 'openai', latencyMs, {
|
|
547
|
+
totalCostUsd: 0.0034,
|
|
548
|
+
});
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
## Model Tier Classification
|
|
552
|
+
|
|
553
|
+
Models are automatically classified into tiers for cost/performance analysis:
|
|
554
|
+
|
|
555
|
+
| Tier | Examples | When to Use |
|
|
556
|
+
| ----------- | -------------------------------------------------------- | ------------------------------ |
|
|
557
|
+
| `fast` | gpt-4o-mini, claude-3-haiku, gemini-flash, gpt-3.5-turbo | High-volume, latency-sensitive |
|
|
558
|
+
| `standard` | gpt-4o, claude-3.5-sonnet, gemini-pro, llama, command | General purpose |
|
|
559
|
+
| `reasoning` | o1, o3-mini, deepseek-r1, claude with extended thinking | Complex reasoning tasks |
|
|
560
|
+
|
|
561
|
+
The tier is inferred automatically from the model name and attached as `[Agent] Model Tier` on every `[Agent] AI Response` event:
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
import {
|
|
565
|
+
inferModelTier,
|
|
566
|
+
TIER_FAST,
|
|
567
|
+
TIER_REASONING,
|
|
568
|
+
TIER_STANDARD,
|
|
569
|
+
} from '@amplitude/ai';
|
|
570
|
+
|
|
571
|
+
inferModelTier('gpt-4o-mini'); // 'fast'
|
|
572
|
+
inferModelTier('claude-3.5-sonnet'); // 'standard'
|
|
573
|
+
inferModelTier('o1-preview'); // 'reasoning'
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
Override the auto-inferred tier for custom or fine-tuned models:
|
|
577
|
+
|
|
578
|
+
```typescript
|
|
579
|
+
s.trackAiMessage(
|
|
580
|
+
response.content,
|
|
581
|
+
'ft:gpt-4o:my-org:custom',
|
|
582
|
+
'openai',
|
|
583
|
+
latencyMs,
|
|
584
|
+
{
|
|
585
|
+
modelTier: 'standard',
|
|
586
|
+
inputTokens: response.usage.prompt_tokens,
|
|
587
|
+
outputTokens: response.usage.completion_tokens,
|
|
588
|
+
},
|
|
589
|
+
);
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
## Provider Wrappers
|
|
593
|
+
|
|
594
|
+
Use instrumented provider wrappers for automatic tracking:
|
|
595
|
+
|
|
596
|
+
| Provider | Class | Package |
|
|
597
|
+
| ----------- | ------------- | ------------------------------- |
|
|
598
|
+
| OpenAI | `OpenAI` | openai |
|
|
599
|
+
| Anthropic | `Anthropic` | @anthropic-ai/sdk |
|
|
600
|
+
| Gemini | `Gemini` | @google/generative-ai |
|
|
601
|
+
| AzureOpenAI | `AzureOpenAI` | openai |
|
|
602
|
+
| Bedrock | `Bedrock` | @aws-sdk/client-bedrock-runtime |
|
|
603
|
+
| Mistral | `Mistral` | @mistralai/mistralai |
|
|
604
|
+
|
|
605
|
+
**Feature coverage by provider:**
|
|
606
|
+
|
|
607
|
+
| Feature | OpenAI | Anthropic | Gemini | AzureOpenAI | Bedrock | Mistral |
|
|
608
|
+
| --------------------- | ------ | --------- | ------ | ----------- | ------- | ------- |
|
|
609
|
+
| Streaming | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
610
|
+
| Tool call tracking | Yes | Yes | No | Yes | Yes | No |
|
|
611
|
+
| TTFB measurement | Yes | Yes | No | Yes | No | No |
|
|
612
|
+
| Cache token stats | Yes | Yes | No | No | No | No |
|
|
613
|
+
| Responses API | Yes | - | - | - | - | - |
|
|
614
|
+
| Reasoning content | Yes | Yes | No | Yes | No | No |
|
|
615
|
+
| System prompt capture | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
616
|
+
| Cost estimation | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
617
|
+
|
|
618
|
+
Provider wrappers use injected `TrackFn` callbacks instead of class hierarchy casts, enabling easier composition and custom tracking logic.
|
|
619
|
+
|
|
620
|
+
Bedrock model IDs like `us.anthropic.claude-3-5-sonnet` are automatically normalized for price lookup (e.g., to `claude-3-5-sonnet`).
|
|
621
|
+
|
|
622
|
+
**OpenAI example:**
|
|
623
|
+
|
|
624
|
+
```typescript
|
|
625
|
+
import { AmplitudeAI, OpenAI } from '@amplitude/ai';
|
|
626
|
+
|
|
627
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
628
|
+
const openai = new OpenAI({
|
|
629
|
+
amplitude: ai,
|
|
630
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
631
|
+
});
|
|
632
|
+
|
|
633
|
+
const agent = ai.agent('my-agent', { userId: 'user-123' });
|
|
634
|
+
const session = agent.session();
|
|
635
|
+
|
|
636
|
+
await session.run(async (s) => {
|
|
637
|
+
const resp = await openai.chat.completions.create({
|
|
638
|
+
model: 'gpt-4',
|
|
639
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
640
|
+
});
|
|
641
|
+
// AI response tracked automatically via wrapper
|
|
642
|
+
});
|
|
643
|
+
```
|
|
644
|
+
|
|
645
|
+
Or wrap an existing client:
|
|
646
|
+
|
|
647
|
+
```typescript
|
|
648
|
+
import { wrap } from '@amplitude/ai';
|
|
649
|
+
import OpenAI from 'openai';
|
|
650
|
+
|
|
651
|
+
const client = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
652
|
+
const instrumented = wrap(client, ai);
|
|
653
|
+
```
|
|
654
|
+
|
|
655
|
+
## Streaming Tracking
|
|
656
|
+
|
|
657
|
+
### Automatic streaming (provider wrappers)
|
|
658
|
+
|
|
659
|
+
Provider wrappers (`OpenAI`, `AzureOpenAI`, `Anthropic`, `Gemini`, `Mistral`, `Bedrock`) automatically detect supported streaming responses and track them transparently. The wrapper intercepts the `AsyncIterable`, accumulates chunks, measures TTFB, and emits an `[Agent] AI Response` event after the stream is fully consumed:
|
|
660
|
+
|
|
661
|
+
```typescript
|
|
662
|
+
const openai = new OpenAI({ amplitude: ai, apiKey: '...' });
|
|
663
|
+
|
|
664
|
+
// Streaming is handled automatically — just iterate the result
|
|
665
|
+
const stream = await openai.chat.completions.create({
|
|
666
|
+
model: 'gpt-4o',
|
|
667
|
+
messages: [{ role: 'user', content: 'Hello' }],
|
|
668
|
+
stream: true,
|
|
669
|
+
});
|
|
670
|
+
|
|
671
|
+
for await (const chunk of stream) {
|
|
672
|
+
process.stdout.write(chunk.choices[0]?.delta?.content ?? '');
|
|
673
|
+
}
|
|
674
|
+
// ^ AI Response event emitted automatically after loop ends
|
|
675
|
+
```
|
|
676
|
+
|
|
677
|
+
### Manual streaming
|
|
678
|
+
|
|
679
|
+
Track streaming responses manually with time-to-first-byte (TTFB) for latency analysis:
|
|
680
|
+
|
|
681
|
+
```typescript
|
|
682
|
+
s.trackAiMessage(fullContent, 'gpt-4o', 'openai', totalMs, {
|
|
683
|
+
isStreaming: true,
|
|
684
|
+
ttfbMs: timeToFirstByte,
|
|
685
|
+
inputTokens: usage.prompt_tokens,
|
|
686
|
+
outputTokens: usage.completion_tokens,
|
|
687
|
+
});
|
|
688
|
+
```
|
|
689
|
+
|
|
690
|
+
The SDK tracks two timing properties for streaming:
|
|
691
|
+
|
|
692
|
+
- `[Agent] Latency Ms` — total wall-clock time from request to final chunk
|
|
693
|
+
- `[Agent] TTFB Ms` — time-to-first-byte, the delay before the first token arrives
|
|
694
|
+
|
|
695
|
+
### StreamingAccumulator
|
|
696
|
+
|
|
697
|
+
For manual streaming, use `StreamingAccumulator` to collect chunks and automatically measure TTFB:
|
|
698
|
+
|
|
699
|
+
```typescript
|
|
700
|
+
import { StreamingAccumulator } from '@amplitude/ai';
|
|
701
|
+
|
|
702
|
+
const accumulator = new StreamingAccumulator();
|
|
703
|
+
|
|
704
|
+
for await (const chunk of stream) {
|
|
705
|
+
const content = chunk.choices[0]?.delta?.content;
|
|
706
|
+
if (content) {
|
|
707
|
+
accumulator.addContent(content);
|
|
708
|
+
}
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
accumulator.setUsage({
|
|
712
|
+
inputTokens: finalUsage.prompt_tokens,
|
|
713
|
+
outputTokens: finalUsage.completion_tokens,
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
s.trackAiMessage(
|
|
717
|
+
accumulator.content,
|
|
718
|
+
'gpt-4o',
|
|
719
|
+
'openai',
|
|
720
|
+
accumulator.elapsedMs,
|
|
721
|
+
{
|
|
722
|
+
isStreaming: true,
|
|
723
|
+
ttfbMs: accumulator.ttfbMs,
|
|
724
|
+
inputTokens: accumulator.inputTokens,
|
|
725
|
+
outputTokens: accumulator.outputTokens,
|
|
726
|
+
finishReason: accumulator.finishReason,
|
|
727
|
+
},
|
|
728
|
+
);
|
|
729
|
+
```
|
|
730
|
+
|
|
731
|
+
The accumulator automatically records TTFB when `addContent()` is called for the first time, and tracks total elapsed time via `elapsedMs`. For streaming errors, call `setError(message)` to set `isError` and `errorMessage`, which are included on the tracked AI Response event.
|
|
732
|
+
|
|
733
|
+
## Attachment Tracking
|
|
734
|
+
|
|
735
|
+
Track files sent with user messages (images, PDFs, URLs):
|
|
736
|
+
|
|
737
|
+
```typescript
|
|
738
|
+
s.trackUserMessage('Analyze this document', {
|
|
739
|
+
attachments: [
|
|
740
|
+
{ type: 'image', name: 'chart.png', size_bytes: 102400 },
|
|
741
|
+
{ type: 'pdf', name: 'report.pdf', size_bytes: 2048576 },
|
|
742
|
+
],
|
|
743
|
+
});
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
The SDK automatically derives aggregate properties from the attachment array:
|
|
747
|
+
|
|
748
|
+
- `[Agent] Has Attachments` — boolean, true when attachments are present
|
|
749
|
+
- `[Agent] Attachment Count` — number of attachments
|
|
750
|
+
- `[Agent] Attachment Types` — deduplicated list of attachment types (e.g., `["image", "pdf"]`)
|
|
751
|
+
- `[Agent] Total Attachment Size Bytes` — sum of all `size_bytes` values
|
|
752
|
+
- `[Agent] Attachments` — serialized JSON of the full attachment metadata
|
|
753
|
+
|
|
754
|
+
Attachments can also be tracked on AI responses (e.g., when the model generates images or files):
|
|
755
|
+
|
|
756
|
+
```typescript
|
|
757
|
+
s.trackAiMessage(response.content, 'gpt-4o', 'openai', latencyMs, {
|
|
758
|
+
attachments: [{ type: 'image', name: 'generated.png', size_bytes: 204800 }],
|
|
759
|
+
});
|
|
760
|
+
```
|
|
761
|
+
|
|
762
|
+
## tool() and observe() HOFs
|
|
763
|
+
|
|
764
|
+
### tool()
|
|
765
|
+
|
|
766
|
+
Wraps an async function to track as `[Agent] Tool Call`:
|
|
767
|
+
|
|
768
|
+
```typescript
|
|
769
|
+
import { tool, ToolCallTracker } from '@amplitude/ai';
|
|
770
|
+
|
|
771
|
+
ToolCallTracker.setAmplitude(ai.amplitude, 'user-123', {
|
|
772
|
+
sessionId: 'sess-1',
|
|
773
|
+
traceId: 'trace-1',
|
|
774
|
+
agentId: 'my-agent',
|
|
775
|
+
privacyConfig: ai.config.toPrivacyConfig(),
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
const fetchWeather = tool(
|
|
779
|
+
async (args: { city: string }) => {
|
|
780
|
+
return await weatherApi.get(args.city);
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
name: 'fetch_weather',
|
|
784
|
+
inputSchema: { type: 'object', properties: { city: { type: 'string' } } },
|
|
785
|
+
timeoutMs: 5000,
|
|
786
|
+
onError: (err, name) => console.error(`Tool ${name} failed:`, err),
|
|
787
|
+
},
|
|
788
|
+
);
|
|
789
|
+
```
|
|
790
|
+
|
|
791
|
+
### observe()
|
|
792
|
+
|
|
793
|
+
Wraps a function to track as `[Agent] Span`:
|
|
794
|
+
|
|
795
|
+
```typescript
|
|
796
|
+
import { observe } from '@amplitude/ai';
|
|
797
|
+
|
|
798
|
+
const enrichData = observe(async (data: unknown) => transform(data), {
|
|
799
|
+
name: 'enrich_data',
|
|
800
|
+
agentId: 'enricher',
|
|
801
|
+
});
|
|
802
|
+
```
|
|
803
|
+
|
|
804
|
+
## Scoring Patterns
|
|
805
|
+
|
|
806
|
+
Track quality feedback from multiple sources using the `score()` method. Scores are emitted as `[Agent] Score` events.
|
|
807
|
+
|
|
808
|
+
### User Feedback (thumbs up/down)
|
|
809
|
+
|
|
810
|
+
```typescript
|
|
811
|
+
s.score('thumbs-up', 1, messageId, { source: 'user' });
|
|
812
|
+
s.score('thumbs-down', 0, messageId, { source: 'user' });
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Numeric Rating
|
|
816
|
+
|
|
817
|
+
```typescript
|
|
818
|
+
s.score('rating', 4, messageId, {
|
|
819
|
+
source: 'user',
|
|
820
|
+
comment: 'Very helpful but slightly verbose',
|
|
821
|
+
});
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
### LLM-as-Judge
|
|
825
|
+
|
|
826
|
+
```typescript
|
|
827
|
+
s.score('quality', 0.85, messageId, {
|
|
828
|
+
source: 'ai',
|
|
829
|
+
comment: 'Clear and accurate response with proper citations',
|
|
830
|
+
});
|
|
831
|
+
```
|
|
832
|
+
|
|
833
|
+
### Session-Level Scoring
|
|
834
|
+
|
|
835
|
+
Score an entire session rather than a single message by setting `targetType` to `'session'`:
|
|
836
|
+
|
|
837
|
+
```typescript
|
|
838
|
+
s.score('session-quality', 0.9, session.sessionId, {
|
|
839
|
+
targetType: 'session',
|
|
840
|
+
source: 'ai',
|
|
841
|
+
});
|
|
842
|
+
```
|
|
843
|
+
|
|
844
|
+
### Score Properties
|
|
845
|
+
|
|
846
|
+
Each `[Agent] Score` event includes:
|
|
847
|
+
|
|
848
|
+
- `[Agent] Score Name` — the name you provide (e.g., `"thumbs-up"`, `"quality"`)
|
|
849
|
+
- `[Agent] Score Value` — numeric value
|
|
850
|
+
- `[Agent] Target ID` — the message ID or session ID being scored
|
|
851
|
+
- `[Agent] Target Type` — `"message"` (default) or `"session"`
|
|
852
|
+
- `[Agent] Evaluation Source` — `"user"` (default) or `"ai"`
|
|
853
|
+
- `[Agent] Comment` — optional free-text comment (respects content mode)
|
|
854
|
+
|
|
855
|
+
## Enrichments
|
|
856
|
+
|
|
857
|
+
### Session Enrichments
|
|
858
|
+
|
|
859
|
+
Attach structured metadata to sessions for analytics. Enrichments are included when the session auto-ends:
|
|
860
|
+
|
|
861
|
+
```typescript
|
|
862
|
+
import {
|
|
863
|
+
RubricScore,
|
|
864
|
+
SessionEnrichments,
|
|
865
|
+
TopicClassification,
|
|
866
|
+
} from '@amplitude/ai';
|
|
867
|
+
|
|
868
|
+
const enrichments = new SessionEnrichments({
|
|
869
|
+
qualityScore: 0.85,
|
|
870
|
+
sentimentScore: 0.7,
|
|
871
|
+
overallOutcome: 'resolved',
|
|
872
|
+
topicClassifications: {
|
|
873
|
+
intent: new TopicClassification({
|
|
874
|
+
l1: 'billing',
|
|
875
|
+
primary: 'billing',
|
|
876
|
+
values: ['billing', 'refund'],
|
|
877
|
+
subcategories: ['REFUND_REQUEST', 'PRICING_QUESTION'],
|
|
878
|
+
}),
|
|
879
|
+
},
|
|
880
|
+
rubrics: [
|
|
881
|
+
new RubricScore({
|
|
882
|
+
name: 'helpfulness',
|
|
883
|
+
score: 4,
|
|
884
|
+
rationale: 'Provided clear step-by-step instructions',
|
|
885
|
+
}),
|
|
886
|
+
new RubricScore({
|
|
887
|
+
name: 'accuracy',
|
|
888
|
+
score: 5,
|
|
889
|
+
rationale: 'All information was factually correct',
|
|
890
|
+
}),
|
|
891
|
+
],
|
|
892
|
+
agentChain: ['orchestrator', 'researcher', 'writer'],
|
|
893
|
+
rootAgentName: 'orchestrator',
|
|
894
|
+
requestComplexity: 'medium',
|
|
895
|
+
});
|
|
896
|
+
|
|
897
|
+
session.setEnrichments(enrichments);
|
|
898
|
+
// Enrichments are included automatically when session.run() completes
|
|
899
|
+
```
|
|
900
|
+
|
|
901
|
+
### Track Enrichments Separately
|
|
902
|
+
|
|
903
|
+
Send enrichments as a standalone event without ending the session:
|
|
904
|
+
|
|
905
|
+
```typescript
|
|
906
|
+
agent.trackSessionEnrichment(enrichments, {
|
|
907
|
+
sessionId: 'sess-abc123',
|
|
908
|
+
});
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### Available Enrichment Fields
|
|
912
|
+
|
|
913
|
+
- **Quality & Sentiment**: `qualityScore`, `sentimentScore`
|
|
914
|
+
- **Outcome**: `overallOutcome`, `hasTaskFailure`, `taskFailureType`, `taskFailureReason`
|
|
915
|
+
- **Topics**: `topicClassifications` — a map of taxonomy name to `TopicClassification`
|
|
916
|
+
- **Rubrics**: `rubrics` — array of `RubricScore` with name, score, rationale, and evidence
|
|
917
|
+
- **Failure Signals**: `hasNegativeFeedback`, `hasDataQualityIssues`, `hasTechnicalFailure`
|
|
918
|
+
- **Error Analysis**: `errorCategories`, `technicalErrorCount`
|
|
919
|
+
- **Behavioral**: `behavioralPatterns`, `negativeFeedbackPhrases`, `dataQualityIssues`
|
|
920
|
+
- **Agent Topology**: `agentChain`, `rootAgentName`
|
|
921
|
+
- **Complexity**: `requestComplexity`
|
|
922
|
+
- **Labels**: `messageLabels` — per-message labels keyed by message ID
|
|
923
|
+
- **Custom**: `customMetadata` — arbitrary key/value data for your own analytics
|
|
924
|
+
|
|
925
|
+
### Message Labels
|
|
926
|
+
|
|
927
|
+
Attach classification labels to individual messages within a session:
|
|
928
|
+
|
|
929
|
+
```typescript
|
|
930
|
+
import { MessageLabel } from '@amplitude/ai';
|
|
931
|
+
|
|
932
|
+
s.trackUserMessage('I want to cancel my subscription', {
|
|
933
|
+
labels: [
|
|
934
|
+
new MessageLabel({
|
|
935
|
+
key: 'intent',
|
|
936
|
+
value: 'cancellation',
|
|
937
|
+
confidence: 0.95,
|
|
938
|
+
}),
|
|
939
|
+
new MessageLabel({
|
|
940
|
+
key: 'sentiment',
|
|
941
|
+
value: 'frustrated',
|
|
942
|
+
confidence: 0.8,
|
|
943
|
+
}),
|
|
944
|
+
],
|
|
945
|
+
});
|
|
946
|
+
```
|
|
947
|
+
|
|
948
|
+
## Debug and Dry-Run Modes
|
|
949
|
+
|
|
950
|
+
### Debug Mode
|
|
951
|
+
|
|
952
|
+
Prints a colored (ANSI) summary of every tracked event to stderr. All 8 event types (User Message, AI Response, Tool Call, Embedding, Span, Session End, Session Enrichment, Score) are formatted. Events are still sent to Amplitude:
|
|
953
|
+
|
|
954
|
+
```typescript
|
|
955
|
+
const ai = new AmplitudeAI({
|
|
956
|
+
apiKey: 'xxx',
|
|
957
|
+
config: new AIConfig({ debug: true }),
|
|
958
|
+
});
|
|
959
|
+
|
|
960
|
+
// stderr output for each event:
|
|
961
|
+
// [amplitude-ai] [Agent] AI Response | user=user-123 session=sess-abc agent=my-agent model=gpt-4o latency=1203ms tokens=150→847 cost=$0.0042
|
|
962
|
+
// [amplitude-ai] [Agent] Tool Call | user=user-123 session=sess-abc agent=my-agent tool=search_db success=true latency=340ms
|
|
963
|
+
// [amplitude-ai] [Agent] User Message | user=user-123 session=sess-abc agent=my-agent
|
|
964
|
+
```
|
|
965
|
+
|
|
966
|
+
### Dry-Run Mode
|
|
967
|
+
|
|
968
|
+
Logs the full event JSON to stderr WITHOUT sending to Amplitude. Events are never transmitted:
|
|
969
|
+
|
|
970
|
+
```typescript
|
|
971
|
+
const ai = new AmplitudeAI({
|
|
972
|
+
apiKey: 'xxx',
|
|
973
|
+
config: new AIConfig({ dryRun: true }),
|
|
974
|
+
});
|
|
975
|
+
|
|
976
|
+
// stderr: full JSON of each event
|
|
977
|
+
// Useful for local development, CI pipelines, and validating event shape
|
|
978
|
+
```
|
|
979
|
+
|
|
980
|
+
### Environment Variable Configuration
|
|
981
|
+
|
|
982
|
+
Both modes can be enabled via environment variables when using auto-instrumentation:
|
|
983
|
+
|
|
984
|
+
```bash
|
|
985
|
+
AMPLITUDE_AI_DEBUG=true amplitude-ai-instrument node app.js
|
|
986
|
+
```
|
|
987
|
+
|
|
988
|
+
## Patching (Zero-Code Instrumentation)
|
|
989
|
+
|
|
990
|
+
Monkey-patch provider SDKs to auto-track without changing call sites:
|
|
991
|
+
|
|
992
|
+
```typescript
|
|
993
|
+
import {
|
|
994
|
+
AmplitudeAI,
|
|
995
|
+
patch,
|
|
996
|
+
patchOpenAI,
|
|
997
|
+
unpatch,
|
|
998
|
+
unpatchOpenAI,
|
|
999
|
+
} from '@amplitude/ai';
|
|
1000
|
+
|
|
1001
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1002
|
+
|
|
1003
|
+
// Patch installed/available providers (OpenAI, Anthropic, Gemini, Mistral, Bedrock)
|
|
1004
|
+
patch({ amplitudeAI: ai });
|
|
1005
|
+
|
|
1006
|
+
// Or patch specific provider
|
|
1007
|
+
patchOpenAI({ amplitudeAI: ai });
|
|
1008
|
+
|
|
1009
|
+
// Unpatch
|
|
1010
|
+
unpatch();
|
|
1011
|
+
unpatchOpenAI();
|
|
1012
|
+
```
|
|
1013
|
+
|
|
1014
|
+
Available patch functions: `patchOpenAI`, `patchAnthropic`, `patchAzureOpenAI`, `patchGemini`, `patchMistral`, `patchBedrock`. Corresponding unpatch for each: `unpatchOpenAI`, `unpatchAnthropic`, `unpatchAzureOpenAI`, `unpatchGemini`, `unpatchMistral`, `unpatchBedrock`.
|
|
1015
|
+
|
|
1016
|
+
`patch()` returns a `string[]` of providers where at least one supported surface was successfully patched (e.g., `['openai', 'anthropic']`), matching the Python SDK's return signature.
|
|
1017
|
+
|
|
1018
|
+
Patch surface notes:
|
|
1019
|
+
|
|
1020
|
+
- OpenAI/Azure OpenAI: `chat.completions.create`, `chat.completions.parse`, and Responses APIs are instrumented (including streaming shapes where exposed by the SDK).
|
|
1021
|
+
- Bedrock: only `ConverseCommand` and `ConverseStreamCommand` are instrumented when patching `client.send`.
|
|
1022
|
+
|
|
1023
|
+
## Auto-Instrumentation CLI
|
|
1024
|
+
|
|
1025
|
+
Preload the register module to auto-patch providers at process start:
|
|
1026
|
+
|
|
1027
|
+
```bash
|
|
1028
|
+
AMPLITUDE_AI_API_KEY=xxx AMPLITUDE_AI_AUTO_PATCH=true amplitude-ai-instrument node app.js
|
|
1029
|
+
```
|
|
1030
|
+
|
|
1031
|
+
Or directly with Node's ESM preload flag:
|
|
1032
|
+
|
|
1033
|
+
```bash
|
|
1034
|
+
AMPLITUDE_AI_API_KEY=xxx AMPLITUDE_AI_AUTO_PATCH=true node --import @amplitude/ai/register app.js
|
|
1035
|
+
```
|
|
1036
|
+
|
|
1037
|
+
Environment variables:
|
|
1038
|
+
|
|
1039
|
+
| Variable | Description |
|
|
1040
|
+
| --------------------------- | ----------------------------------------------- |
|
|
1041
|
+
| `AMPLITUDE_AI_API_KEY` | Required for auto-patch |
|
|
1042
|
+
| `AMPLITUDE_AI_AUTO_PATCH` | Must be `"true"` to enable |
|
|
1043
|
+
| `AMPLITUDE_AI_CONTENT_MODE` | `full`, `metadata_only`, or `customer_enriched` |
|
|
1044
|
+
| `AMPLITUDE_AI_DEBUG` | `"true"` for debug output to stderr |
|
|
1045
|
+
|
|
1046
|
+
### Init and Doctor CLI
|
|
1047
|
+
|
|
1048
|
+
Scaffold a minimal setup:
|
|
1049
|
+
|
|
1050
|
+
```bash
|
|
1051
|
+
amplitude-ai init
|
|
1052
|
+
```
|
|
1053
|
+
|
|
1054
|
+
Validate setup (env, provider deps, mock event capture, mock flush path):
|
|
1055
|
+
|
|
1056
|
+
```bash
|
|
1057
|
+
amplitude-ai doctor
|
|
1058
|
+
```
|
|
1059
|
+
|
|
1060
|
+
When a check fails, `doctor` includes an actionable `fix` field with a copy-pasteable remediation command (e.g., `export AMPLITUDE_AI_API_KEY=...`, `pnpm add openai`).
|
|
1061
|
+
|
|
1062
|
+
Useful flags:
|
|
1063
|
+
|
|
1064
|
+
- `amplitude-ai init --dry-run`
|
|
1065
|
+
- `amplitude-ai init --force`
|
|
1066
|
+
- `amplitude-ai doctor --no-mock-check`
|
|
1067
|
+
|
|
1068
|
+
### Status
|
|
1069
|
+
|
|
1070
|
+
Show the installed SDK version, detected provider packages, and environment variable configuration at a glance:
|
|
1071
|
+
|
|
1072
|
+
```bash
|
|
1073
|
+
amplitude-ai status
|
|
1074
|
+
```
|
|
1075
|
+
|
|
1076
|
+
### Shell Completions
|
|
1077
|
+
|
|
1078
|
+
Enable tab-completion for all CLI commands and flags:
|
|
1079
|
+
|
|
1080
|
+
```bash
|
|
1081
|
+
# bash
|
|
1082
|
+
eval "$(amplitude-ai-completions bash)"
|
|
1083
|
+
|
|
1084
|
+
# zsh
|
|
1085
|
+
eval "$(amplitude-ai-completions zsh)"
|
|
1086
|
+
```
|
|
1087
|
+
|
|
1088
|
+
### MCP Server
|
|
1089
|
+
|
|
1090
|
+
Run the SDK-local MCP server over stdio:
|
|
1091
|
+
|
|
1092
|
+
```bash
|
|
1093
|
+
amplitude-ai mcp
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
MCP surface:
|
|
1097
|
+
|
|
1098
|
+
| Tool | Description |
|
|
1099
|
+
| ------------------------- | ------------------------------------------------------------- |
|
|
1100
|
+
| `get_event_schema` | Return the full event schema and property definitions |
|
|
1101
|
+
| `get_integration_pattern` | Return canonical instrumentation code patterns |
|
|
1102
|
+
| `validate_setup` | Check env vars and dependency presence |
|
|
1103
|
+
| `suggest_instrumentation` | Context-aware next steps based on your framework and provider |
|
|
1104
|
+
| `validate_file` | Analyze source code to detect uninstrumented LLM call sites |
|
|
1105
|
+
|
|
1106
|
+
Resources: `amplitude-ai://event-schema`, `amplitude-ai://integration-patterns`
|
|
1107
|
+
|
|
1108
|
+
Prompt: `instrument_app` — guided walkthrough for instrumenting an application
|
|
1109
|
+
|
|
1110
|
+
### Examples and Cursor Skill
|
|
1111
|
+
|
|
1112
|
+
- Mock-based examples demonstrating the event model (also used as CI smoke tests):
|
|
1113
|
+
- `examples/zero-code.ts`
|
|
1114
|
+
- `examples/wrap-openai.ts`
|
|
1115
|
+
- `examples/multi-agent.ts`
|
|
1116
|
+
- `examples/framework-integration.ts`
|
|
1117
|
+
- Real provider examples (require API keys):
|
|
1118
|
+
- `examples/real-openai.ts` — end-to-end OpenAI integration with session tracking and flush
|
|
1119
|
+
- `examples/real-anthropic.ts` — end-to-end Anthropic integration with session tracking and flush
|
|
1120
|
+
- Cursor skill:
|
|
1121
|
+
- `.cursor/skills/instrument-with-amplitude-ai/SKILL.md`
|
|
1122
|
+
|
|
1123
|
+
## Integrations
|
|
1124
|
+
|
|
1125
|
+
### LangChain
|
|
1126
|
+
|
|
1127
|
+
```typescript
|
|
1128
|
+
import { AmplitudeAI, AmplitudeCallbackHandler } from '@amplitude/ai';
|
|
1129
|
+
|
|
1130
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1131
|
+
const handler = new AmplitudeCallbackHandler({
|
|
1132
|
+
amplitudeAI: ai,
|
|
1133
|
+
userId: 'user-123',
|
|
1134
|
+
sessionId: 'sess-1',
|
|
1135
|
+
});
|
|
1136
|
+
|
|
1137
|
+
// Pass handler to LangChain callbacks
|
|
1138
|
+
```
|
|
1139
|
+
|
|
1140
|
+
### OpenTelemetry
|
|
1141
|
+
|
|
1142
|
+
```typescript
|
|
1143
|
+
import { AmplitudeAgentExporter, AmplitudeGenAIExporter } from '@amplitude/ai';
|
|
1144
|
+
|
|
1145
|
+
// Exporters for OTLP-compatible pipelines
|
|
1146
|
+
```
|
|
1147
|
+
|
|
1148
|
+
Spans with `gen_ai.provider.name` or `gen_ai.system` are treated as GenAI spans; other spans are ignored.
|
|
1149
|
+
|
|
1150
|
+
**What the OTEL bridge maps:** model (prefers `gen_ai.response.model`), provider, tokens (input/output/total), cache tokens (`gen_ai.usage.cache_read.input_tokens`, `gen_ai.usage.cache_creation.input_tokens`), cache-aware cost, latency, temperature, top_p, max_output_tokens, finish reason, errors, user messages (from `gen_ai.input.messages`).
|
|
1151
|
+
|
|
1152
|
+
**Not available via OTEL (use native wrappers):** reasoning content/tokens, TTFB, streaming detection, implicit feedback, file attachments, event graph linking (parent_message_id).
|
|
1153
|
+
|
|
1154
|
+
### LlamaIndex
|
|
1155
|
+
|
|
1156
|
+
```typescript
|
|
1157
|
+
import {
|
|
1158
|
+
AmplitudeLlamaIndexHandler,
|
|
1159
|
+
createAmplitudeLlamaIndexHandler,
|
|
1160
|
+
} from '@amplitude/ai';
|
|
1161
|
+
```
|
|
1162
|
+
|
|
1163
|
+
### OpenAI Agents SDK
|
|
1164
|
+
|
|
1165
|
+
```typescript
|
|
1166
|
+
import { AmplitudeTracingProcessor } from '@amplitude/ai';
|
|
1167
|
+
```
|
|
1168
|
+
|
|
1169
|
+
### Anthropic Tool Use
|
|
1170
|
+
|
|
1171
|
+
```typescript
|
|
1172
|
+
import { AmplitudeToolLoop } from '@amplitude/ai';
|
|
1173
|
+
```
|
|
1174
|
+
|
|
1175
|
+
### CrewAI (Python-only)
|
|
1176
|
+
|
|
1177
|
+
```typescript
|
|
1178
|
+
import { AmplitudeCrewAIHooks } from '@amplitude/ai';
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
In Node.js, `AmplitudeCrewAIHooks` throws a `ProviderError` by design. Use LangChain or OpenTelemetry integrations instead.
|
|
1182
|
+
|
|
1183
|
+
## Integration Patterns
|
|
1184
|
+
|
|
1185
|
+
### Pattern A: Single-Request API Endpoint
|
|
1186
|
+
|
|
1187
|
+
For serverless functions or API endpoints that handle one request at a time. The key requirement is flushing events before the handler returns:
|
|
1188
|
+
|
|
1189
|
+
```typescript
|
|
1190
|
+
import { AmplitudeAI } from '@amplitude/ai';
|
|
1191
|
+
|
|
1192
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1193
|
+
|
|
1194
|
+
app.post('/chat', async (req, res) => {
|
|
1195
|
+
const agent = ai.agent('api-handler', { userId: req.userId });
|
|
1196
|
+
const session = agent.session({ sessionId: req.sessionId });
|
|
1197
|
+
|
|
1198
|
+
const result = await session.run(async (s) => {
|
|
1199
|
+
s.trackUserMessage(req.body.message);
|
|
1200
|
+
|
|
1201
|
+
const start = performance.now();
|
|
1202
|
+
const response = await openai.chat.completions.create({
|
|
1203
|
+
model: 'gpt-4o',
|
|
1204
|
+
messages: req.body.messages,
|
|
1205
|
+
});
|
|
1206
|
+
const latencyMs = performance.now() - start;
|
|
1207
|
+
|
|
1208
|
+
s.trackAiMessage(
|
|
1209
|
+
response.choices[0].message.content ?? '',
|
|
1210
|
+
'gpt-4o',
|
|
1211
|
+
'openai',
|
|
1212
|
+
latencyMs,
|
|
1213
|
+
{
|
|
1214
|
+
inputTokens: response.usage?.prompt_tokens,
|
|
1215
|
+
outputTokens: response.usage?.completion_tokens,
|
|
1216
|
+
},
|
|
1217
|
+
);
|
|
1218
|
+
|
|
1219
|
+
return response.choices[0].message.content;
|
|
1220
|
+
});
|
|
1221
|
+
|
|
1222
|
+
await ai.flush();
|
|
1223
|
+
res.json({ response: result });
|
|
1224
|
+
});
|
|
1225
|
+
```
|
|
1226
|
+
|
|
1227
|
+
### Pattern B: Long-Lived Session (Chatbot)
|
|
1228
|
+
|
|
1229
|
+
For multi-turn conversations where the session spans many request/response cycles. Create the session once and reuse it across turns:
|
|
1230
|
+
|
|
1231
|
+
```typescript
|
|
1232
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1233
|
+
const agent = ai.agent('chatbot', { userId: 'user-123', env: 'production' });
|
|
1234
|
+
|
|
1235
|
+
// Session persists across multiple turns
|
|
1236
|
+
const session = agent.session({ sessionId: conversationId });
|
|
1237
|
+
|
|
1238
|
+
await session.run(async (s) => {
|
|
1239
|
+
// Turn 1
|
|
1240
|
+
s.trackUserMessage('What is Amplitude?');
|
|
1241
|
+
const resp1 = await llm.chat('What is Amplitude?');
|
|
1242
|
+
s.trackAiMessage(resp1.content, 'gpt-4o', 'openai', resp1.latencyMs, {
|
|
1243
|
+
inputTokens: resp1.usage.input,
|
|
1244
|
+
outputTokens: resp1.usage.output,
|
|
1245
|
+
});
|
|
1246
|
+
|
|
1247
|
+
// Turn 2
|
|
1248
|
+
s.trackUserMessage('How does it track events?');
|
|
1249
|
+
const resp2 = await llm.chat('How does it track events?');
|
|
1250
|
+
s.trackAiMessage(resp2.content, 'gpt-4o', 'openai', resp2.latencyMs, {
|
|
1251
|
+
inputTokens: resp2.usage.input,
|
|
1252
|
+
outputTokens: resp2.usage.output,
|
|
1253
|
+
});
|
|
1254
|
+
|
|
1255
|
+
// Score the conversation
|
|
1256
|
+
s.score('helpfulness', 0.9, session.sessionId, {
|
|
1257
|
+
targetType: 'session',
|
|
1258
|
+
source: 'ai',
|
|
1259
|
+
});
|
|
1260
|
+
});
|
|
1261
|
+
// Session auto-ends here with all enrichments
|
|
1262
|
+
```
|
|
1263
|
+
|
|
1264
|
+
### Pattern C: Multi-Agent Orchestration
|
|
1265
|
+
|
|
1266
|
+
For architectures where a parent agent delegates to specialized child agents. Each child inherits context from the parent:
|
|
1267
|
+
|
|
1268
|
+
```typescript
|
|
1269
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1270
|
+
const orchestrator = ai.agent('orchestrator', {
|
|
1271
|
+
userId: 'user-123',
|
|
1272
|
+
env: 'production',
|
|
1273
|
+
});
|
|
1274
|
+
|
|
1275
|
+
const researcher = orchestrator.child('researcher');
|
|
1276
|
+
const writer = orchestrator.child('writer');
|
|
1277
|
+
const reviewer = orchestrator.child('reviewer');
|
|
1278
|
+
|
|
1279
|
+
const session = orchestrator.session({ userId: 'user-123' });
|
|
1280
|
+
|
|
1281
|
+
await session.run(async (s) => {
|
|
1282
|
+
s.trackUserMessage('Write a blog post about TypeScript generics');
|
|
1283
|
+
|
|
1284
|
+
// Research phase
|
|
1285
|
+
const researchResult = await doResearch(researcher, s);
|
|
1286
|
+
|
|
1287
|
+
// Writing phase
|
|
1288
|
+
const draft = await writeDraft(writer, s, researchResult);
|
|
1289
|
+
|
|
1290
|
+
// Review phase
|
|
1291
|
+
const finalPost = await reviewDraft(reviewer, s, draft);
|
|
1292
|
+
|
|
1293
|
+
s.trackAiMessage(finalPost, 'gpt-4o', 'openai', totalLatencyMs, {
|
|
1294
|
+
inputTokens: totalInput,
|
|
1295
|
+
outputTokens: totalOutput,
|
|
1296
|
+
});
|
|
1297
|
+
});
|
|
1298
|
+
|
|
1299
|
+
// Each child agent's events include:
|
|
1300
|
+
// [Agent] Agent ID = "researcher" | "writer" | "reviewer"
|
|
1301
|
+
// [Agent] Parent Agent ID = "orchestrator"
|
|
1302
|
+
// [Agent] Agent Chain (on enrichments) = ["orchestrator", "researcher", "writer", "reviewer"]
|
|
1303
|
+
```
|
|
1304
|
+
|
|
1305
|
+
## Serverless Environments
|
|
1306
|
+
|
|
1307
|
+
**Critical**: Always `await ai.flush()` before your handler returns. Without this, events buffered in memory are lost when the runtime freezes or terminates:
|
|
1308
|
+
|
|
1309
|
+
```typescript
|
|
1310
|
+
export async function handler(event: APIGatewayEvent) {
|
|
1311
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1312
|
+
const agent = ai.agent('api-handler', {
|
|
1313
|
+
userId: event.requestContext.authorizer?.userId,
|
|
1314
|
+
});
|
|
1315
|
+
|
|
1316
|
+
const session = agent.session();
|
|
1317
|
+
|
|
1318
|
+
const result = await session.run(async (s) => {
|
|
1319
|
+
s.trackUserMessage(JSON.parse(event.body ?? '{}').message ?? '');
|
|
1320
|
+
|
|
1321
|
+
const start = performance.now();
|
|
1322
|
+
const response = await callLLM(JSON.parse(event.body ?? '{}').message);
|
|
1323
|
+
const latencyMs = performance.now() - start;
|
|
1324
|
+
|
|
1325
|
+
s.trackAiMessage(response.content, response.model, 'openai', latencyMs, {
|
|
1326
|
+
inputTokens: response.usage.prompt_tokens,
|
|
1327
|
+
outputTokens: response.usage.completion_tokens,
|
|
1328
|
+
});
|
|
1329
|
+
|
|
1330
|
+
return response.content;
|
|
1331
|
+
});
|
|
1332
|
+
|
|
1333
|
+
await ai.flush(); // Without this, events may be lost
|
|
1334
|
+
return { statusCode: 200, body: JSON.stringify({ response: result }) };
|
|
1335
|
+
}
|
|
1336
|
+
```
|
|
1337
|
+
|
|
1338
|
+
### Flush vs Shutdown
|
|
1339
|
+
|
|
1340
|
+
- `ai.flush()` — sends all buffered events and returns a promise. Use in serverless handlers and API endpoints where you need to ensure delivery before responding.
|
|
1341
|
+
- `ai.shutdown()` — flushes and closes the underlying Amplitude client. Only needed if you created the client via `apiKey` (not when passing your own instance). Call on process exit (e.g., `SIGTERM` handler).
|
|
1342
|
+
|
|
1343
|
+
```typescript
|
|
1344
|
+
process.on('SIGTERM', () => {
|
|
1345
|
+
ai.shutdown();
|
|
1346
|
+
process.exit(0);
|
|
1347
|
+
});
|
|
1348
|
+
```
|
|
1349
|
+
|
|
1350
|
+
## Error Handling and Reliability
|
|
1351
|
+
|
|
1352
|
+
- **Non-throwing**: All `track*` methods catch and log errors internally. Your application code is never interrupted by tracking failures.
|
|
1353
|
+
- **Buffering**: Events are buffered and sent in batches by the underlying `@amplitude/analytics-node` SDK.
|
|
1354
|
+
- **Retry**: Failed sends are automatically retried by the transport layer.
|
|
1355
|
+
- **Validation**: Enable `validate: true` in `AIConfig` to get early validation errors for missing required fields (userId, sessionId, etc.). Validation errors throw `ValidationError` so you can catch them during development.
|
|
1356
|
+
- **Graceful degradation**: If the Amplitude service is unreachable, events are silently dropped after retries are exhausted. Your LLM application continues operating normally.
|
|
1357
|
+
|
|
1358
|
+
```typescript
|
|
1359
|
+
import { AIConfig, AmplitudeAI, ValidationError } from '@amplitude/ai';
|
|
1360
|
+
|
|
1361
|
+
const ai = new AmplitudeAI({
|
|
1362
|
+
apiKey: 'xxx',
|
|
1363
|
+
config: new AIConfig({ validate: true }),
|
|
1364
|
+
});
|
|
1365
|
+
|
|
1366
|
+
try {
|
|
1367
|
+
ai.trackUserMessage({ userId: '', content: 'Hello', sessionId: 'sess-1' });
|
|
1368
|
+
} catch (e) {
|
|
1369
|
+
if (e instanceof ValidationError) {
|
|
1370
|
+
console.error('Invalid tracking call:', e.message);
|
|
1371
|
+
// "userId must be a non-empty string, got "
|
|
1372
|
+
}
|
|
1373
|
+
}
|
|
1374
|
+
```
|
|
1375
|
+
|
|
1376
|
+
## Testing
|
|
1377
|
+
|
|
1378
|
+
Use `MockAmplitudeAI` for unit tests:
|
|
1379
|
+
|
|
1380
|
+
```typescript
|
|
1381
|
+
import { MockAmplitudeAI } from '@amplitude/ai';
|
|
1382
|
+
|
|
1383
|
+
const mock = new MockAmplitudeAI();
|
|
1384
|
+
|
|
1385
|
+
const agent = mock.agent('test-agent', { userId: 'user-1' });
|
|
1386
|
+
const session = agent.session({ sessionId: 'sess-1', userId: 'user-1' });
|
|
1387
|
+
|
|
1388
|
+
await session.run(async (s) => {
|
|
1389
|
+
s.trackUserMessage('Hello');
|
|
1390
|
+
s.trackAiMessage('Hi!', 'gpt-4', 'openai', 100);
|
|
1391
|
+
});
|
|
1392
|
+
|
|
1393
|
+
mock.assertEventTracked('[Agent] User Message', { userId: 'user-1' });
|
|
1394
|
+
mock.assertEventTracked('[Agent] AI Response', { userId: 'user-1' });
|
|
1395
|
+
mock.assertSessionClosed('sess-1');
|
|
1396
|
+
|
|
1397
|
+
mock.reset();
|
|
1398
|
+
```
|
|
1399
|
+
|
|
1400
|
+
## Troubleshooting
|
|
1401
|
+
|
|
1402
|
+
| Symptom | Cause | Fix |
|
|
1403
|
+
| -------------------------------------------------- | ----------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1404
|
+
| No events in Amplitude | API key not set or incorrect | Run `amplitude-ai doctor` — it checks `AMPLITUDE_AI_API_KEY` and reports a fix command |
|
|
1405
|
+
| Events tracked but `[Agent] Cost USD` is $0 | Model not in the pricing database, or `total_cost_usd` not passed | Pass `totalCostUsd` explicitly, or check that `@pydantic/genai-prices` / `genai-prices` is installed |
|
|
1406
|
+
| `patch()` doesn't instrument calls | `patch()` called after the provider client was created | Call `patch()` before importing or instantiating provider clients |
|
|
1407
|
+
| Session context missing on events | LLM calls made outside `session.run()` | Wrap your LLM calls inside `session.run(async () => { ... })` |
|
|
1408
|
+
| `flush()` hangs or times out in serverless | Process exits before flush completes | Use `await ai.flush()` before returning from your Lambda/Cloud Function handler |
|
|
1409
|
+
| `wrap()` TypeScript type errors | Passing a non-supported client type | `wrap()` only supports OpenAI, AzureOpenAI, and Anthropic clients; use provider classes for others |
|
|
1410
|
+
| `MockAmplitudeAI` events are empty | Tracking calls not inside a session context | Use `mock.agent(...).session(...).run(...)` to wrap tracked calls |
|
|
1411
|
+
| `Cannot find module 'openai'` in Turbopack/Webpack | Bundler rewrites `import.meta.url`, breaking dynamic `require()` | Pass the provider module directly: `new OpenAI({ amplitude: ai, apiKey, openaiModule: OpenAISDK })`. Same pattern for `Anthropic`, `Gemini`, etc. See each provider's `<name>Module` option. |
|
|
1412
|
+
|
|
1413
|
+
Run `amplitude-ai doctor` for automated environment diagnostics with fix suggestions.
|
|
1414
|
+
|
|
1415
|
+
## Context Propagation
|
|
1416
|
+
|
|
1417
|
+
For distributed tracing, inject context into outgoing request headers and extract on the receiving side:
|
|
1418
|
+
|
|
1419
|
+
```typescript
|
|
1420
|
+
import { randomUUID } from 'node:crypto';
|
|
1421
|
+
import {
|
|
1422
|
+
extractContext,
|
|
1423
|
+
injectContext,
|
|
1424
|
+
runWithContextAsync,
|
|
1425
|
+
SessionContext,
|
|
1426
|
+
} from '@amplitude/ai';
|
|
1427
|
+
|
|
1428
|
+
// Outgoing request
|
|
1429
|
+
const headers = injectContext();
|
|
1430
|
+
fetch(url, { headers });
|
|
1431
|
+
|
|
1432
|
+
// Receiving side
|
|
1433
|
+
const extracted = extractContext(req.headers);
|
|
1434
|
+
const ctx = new SessionContext({
|
|
1435
|
+
sessionId: extracted.sessionId ?? randomUUID(),
|
|
1436
|
+
traceId: extracted.traceId ?? null,
|
|
1437
|
+
userId: extracted.userId ?? null,
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
await runWithContextAsync(ctx, async () => {
|
|
1441
|
+
// Context available via getActiveContext()
|
|
1442
|
+
});
|
|
1443
|
+
```
|
|
1444
|
+
|
|
1445
|
+
## Middleware
|
|
1446
|
+
|
|
1447
|
+
Express-compatible middleware for automatic session tracking:
|
|
1448
|
+
|
|
1449
|
+
```typescript
|
|
1450
|
+
import { randomUUID } from 'node:crypto';
|
|
1451
|
+
import { AmplitudeAI, createAmplitudeAIMiddleware } from '@amplitude/ai';
|
|
1452
|
+
import express from 'express';
|
|
1453
|
+
|
|
1454
|
+
const ai = new AmplitudeAI({ apiKey: process.env.AMPLITUDE_AI_API_KEY! });
|
|
1455
|
+
|
|
1456
|
+
const app = express();
|
|
1457
|
+
app.use(
|
|
1458
|
+
createAmplitudeAIMiddleware({
|
|
1459
|
+
amplitudeAI: ai,
|
|
1460
|
+
userIdResolver: (req) =>
|
|
1461
|
+
(req as { headers: { 'x-user-id'?: string } }).headers['x-user-id'] ??
|
|
1462
|
+
null,
|
|
1463
|
+
sessionIdResolver: (req) =>
|
|
1464
|
+
(req as { headers: { 'x-session-id'?: string } }).headers[
|
|
1465
|
+
'x-session-id'
|
|
1466
|
+
] ?? randomUUID(),
|
|
1467
|
+
agentId: 'api-server',
|
|
1468
|
+
env: process.env.NODE_ENV ?? 'development',
|
|
1469
|
+
}),
|
|
1470
|
+
);
|
|
1471
|
+
|
|
1472
|
+
app.post('/chat', async (req, res) => {
|
|
1473
|
+
// Session context available; trackUserMessage/trackAiMessage inherit sessionId, traceId
|
|
1474
|
+
});
|
|
1475
|
+
```
|
|
1476
|
+
|
|
1477
|
+
## Bulk Conversation Import
|
|
1478
|
+
|
|
1479
|
+
Use `trackConversation()` to import an entire conversation history in one call. Each message in the array is tracked as either a `[Agent] User Message` or `[Agent] AI Response` event, with turn IDs auto-incremented:
|
|
1480
|
+
|
|
1481
|
+
```typescript
|
|
1482
|
+
import { trackConversation } from '@amplitude/ai';
|
|
1483
|
+
import * as amplitude from '@amplitude/analytics-node';
|
|
1484
|
+
|
|
1485
|
+
trackConversation({
|
|
1486
|
+
amplitude,
|
|
1487
|
+
userId: 'user-123',
|
|
1488
|
+
sessionId: 'sess-abc',
|
|
1489
|
+
agentId: 'support-bot',
|
|
1490
|
+
messages: [
|
|
1491
|
+
{ role: 'user', content: 'How do I reset my password?' },
|
|
1492
|
+
{
|
|
1493
|
+
role: 'assistant',
|
|
1494
|
+
content: 'Go to Settings > Security > Reset Password.',
|
|
1495
|
+
model: 'gpt-4o',
|
|
1496
|
+
provider: 'openai',
|
|
1497
|
+
latency_ms: 1200,
|
|
1498
|
+
input_tokens: 15,
|
|
1499
|
+
output_tokens: 42,
|
|
1500
|
+
total_cost_usd: 0.002,
|
|
1501
|
+
},
|
|
1502
|
+
{ role: 'user', content: 'Thanks, that worked!' },
|
|
1503
|
+
{
|
|
1504
|
+
role: 'assistant',
|
|
1505
|
+
content: 'Glad I could help!',
|
|
1506
|
+
model: 'gpt-4o',
|
|
1507
|
+
provider: 'openai',
|
|
1508
|
+
latency_ms: 800,
|
|
1509
|
+
input_tokens: 10,
|
|
1510
|
+
output_tokens: 8,
|
|
1511
|
+
},
|
|
1512
|
+
],
|
|
1513
|
+
});
|
|
1514
|
+
```
|
|
1515
|
+
|
|
1516
|
+
This is useful for backfilling historical conversations or importing data from external systems. The function accepts all the same context fields (`agentId`, `env`, `customerOrgId`, etc.) as the individual tracking methods.
|
|
1517
|
+
|
|
1518
|
+
## Event Schema
|
|
1519
|
+
|
|
1520
|
+
| Event Type | Source | Description |
|
|
1521
|
+
| ------------------------------ | ------ | ------------------------------------------------------------------------------- |
|
|
1522
|
+
| `[Agent] User Message` | SDK | User sent a message |
|
|
1523
|
+
| `[Agent] AI Response` | SDK | AI model returned a response |
|
|
1524
|
+
| `[Agent] Tool Call` | SDK | Tool/function was invoked |
|
|
1525
|
+
| `[Agent] Embedding` | SDK | Embedding was generated |
|
|
1526
|
+
| `[Agent] Span` | SDK | Span (e.g. RAG step, transform) |
|
|
1527
|
+
| `[Agent] Session End` | SDK | Session ended |
|
|
1528
|
+
| `[Agent] Session Enrichment` | SDK | Session-level enrichment data |
|
|
1529
|
+
| `[Agent] Score` | Both | Evaluation score (quality, sentiment, etc.) |
|
|
1530
|
+
| `[Agent] Session Evaluation` | Server | Session-level summary: outcome, turn count, flags, cost. Emitted automatically. |
|
|
1531
|
+
| `[Agent] Topic Classification` | Server | One event per topic model per session. Emitted automatically. |
|
|
1532
|
+
|
|
1533
|
+
## Event Property Reference
|
|
1534
|
+
|
|
1535
|
+
All event properties are prefixed with `[Agent]` (except `[Amplitude] Session Replay ID`). This reference is auto-generated and matches what gets registered in Amplitude's data catalog via the `amplitude-ai-register-catalog` CLI.
|
|
1536
|
+
|
|
1537
|
+
<!-- BEGIN EVENT PROPERTY REFERENCE -->
|
|
1538
|
+
|
|
1539
|
+
### Common Properties (present on all SDK events)
|
|
1540
|
+
|
|
1541
|
+
| Property | Type | Required | Description |
|
|
1542
|
+
| ------------------------- | ------ | -------- | ----------------------------------------------------------------------------------------------------------------------------- |
|
|
1543
|
+
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
|
|
1544
|
+
| `[Agent] Trace ID` | string | No | Identifies one user-message-to-AI-response cycle within a session. |
|
|
1545
|
+
| `[Agent] Turn ID` | number | No | Monotonically increasing counter for event ordering within a session. |
|
|
1546
|
+
| `[Agent] Agent ID` | string | No | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
|
|
1547
|
+
| `[Agent] Parent Agent ID` | string | No | For multi-agent orchestration: the agent that delegated to this agent. |
|
|
1548
|
+
| `[Agent] Customer Org ID` | string | No | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
|
|
1549
|
+
| `[Agent] Agent Version` | string | No | Agent code version (e.g., 'v4.2'). Enables version-over-version quality comparison. |
|
|
1550
|
+
| `[Agent] Context` | string | No | Serialized JSON dict of arbitrary segmentation dimensions (experiment_variant, surface, feature_flag, prompt_revision, etc.). |
|
|
1551
|
+
| `[Agent] Env` | string | No | Deployment environment: 'production', 'staging', or 'dev'. |
|
|
1552
|
+
| `[Agent] SDK Version` | string | Yes | Version of the amplitude-ai SDK that produced this event. |
|
|
1553
|
+
| `[Agent] Runtime` | string | Yes | SDK runtime: 'python' or 'node'. |
|
|
1554
|
+
|
|
1555
|
+
### User Message Properties
|
|
1556
|
+
|
|
1557
|
+
Event-specific properties for `[Agent] User Message` (in addition to common properties above).
|
|
1558
|
+
|
|
1559
|
+
| Property | Type | Required | Description |
|
|
1560
|
+
| ------------------------------------- | -------- | -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1561
|
+
| `[Agent] Message ID` | string | Yes | Unique identifier for this message event (UUID). Used to link scores and tool calls back to specific messages. |
|
|
1562
|
+
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
|
|
1563
|
+
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
|
|
1564
|
+
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
|
|
1565
|
+
| `[Agent] Is Regeneration` | boolean | No | Whether the user requested the AI regenerate a previous response. |
|
|
1566
|
+
| `[Agent] Is Edit` | boolean | No | Whether the user edited a previous message and resubmitted. |
|
|
1567
|
+
| `[Agent] Edited Message ID` | string | No | The message_id of the original message that was edited (links the edit to the original). |
|
|
1568
|
+
| `[Agent] Has Attachments` | boolean | No | Whether this message includes file attachments (uploads, images, etc.). |
|
|
1569
|
+
| `[Agent] Attachment Types` | string[] | No | Distinct attachment types (e.g., 'pdf', 'image', 'csv'). Serialized JSON array. |
|
|
1570
|
+
| `[Agent] Attachment Count` | number | No | Number of file attachments included with this message. |
|
|
1571
|
+
| `[Agent] Total Attachment Size Bytes` | number | No | Total size of all attachments in bytes. |
|
|
1572
|
+
| `[Agent] Attachments` | string | No | Serialized JSON array of attachment metadata (type, name, size_bytes, mime_type). Only metadata, never file content. |
|
|
1573
|
+
| `[Agent] Message Labels` | string | No | Serialized JSON array of MessageLabel objects (key-value pairs with optional confidence). Used for routing tags, classifier output, business context. |
|
|
1574
|
+
|
|
1575
|
+
### AI Response Properties
|
|
1576
|
+
|
|
1577
|
+
Event-specific properties for `[Agent] AI Response` (in addition to common properties above).
|
|
1578
|
+
|
|
1579
|
+
| Property | Type | Required | Description |
|
|
1580
|
+
| ------------------------------------- | -------- | -------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1581
|
+
| `[Agent] Message ID` | string | Yes | Unique identifier for this message event (UUID). Used to link scores and tool calls back to specific messages. |
|
|
1582
|
+
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
|
|
1583
|
+
| `[Agent] Model Name` | string | Yes | LLM model identifier (e.g., 'gpt-4o', 'claude-sonnet-4-20250514'). |
|
|
1584
|
+
| `[Agent] Provider` | string | Yes | LLM provider name (e.g., 'openai', 'anthropic', 'google', 'mistral', 'bedrock'). |
|
|
1585
|
+
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
|
|
1586
|
+
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
|
|
1587
|
+
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
|
|
1588
|
+
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
|
|
1589
|
+
| `[Agent] Span Kind` | string | No | Classification of the span type for OTEL bridge compatibility. |
|
|
1590
|
+
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
|
|
1591
|
+
| `[Agent] TTFB Ms` | number | No | Time to first byte/token in milliseconds. Measures perceived responsiveness for streaming. |
|
|
1592
|
+
| `[Agent] Input Tokens` | number | No | Number of input/prompt tokens consumed by this LLM call. |
|
|
1593
|
+
| `[Agent] Output Tokens` | number | No | Number of output/completion tokens generated by this LLM call. |
|
|
1594
|
+
| `[Agent] Total Tokens` | number | No | Total tokens consumed (input + output). |
|
|
1595
|
+
| `[Agent] Reasoning Tokens` | number | No | Tokens consumed by reasoning/thinking (o1, o3, extended thinking models). |
|
|
1596
|
+
| `[Agent] Cache Read Tokens` | number | No | Input tokens served from the provider's prompt cache (cheaper rate). Used for cache-aware cost calculation. |
|
|
1597
|
+
| `[Agent] Cache Creation Tokens` | number | No | Input tokens that created new prompt cache entries. |
|
|
1598
|
+
| `[Agent] Cost USD` | number | No | Estimated cost in USD for this LLM call. Cache-aware when cache token counts are provided. |
|
|
1599
|
+
| `[Agent] Finish Reason` | string | No | Why the model stopped generating: 'stop', 'end_turn', 'tool_use', 'length', 'content_filter', etc. |
|
|
1600
|
+
| `[Agent] Tool Calls` | string | No | Serialized JSON array of tool call requests made by the AI in this response. |
|
|
1601
|
+
| `[Agent] Has Reasoning` | boolean | No | Whether the AI response included reasoning/thinking content. |
|
|
1602
|
+
| `[Agent] Reasoning Content` | string | No | The AI's reasoning/thinking content (when available and content_mode permits). |
|
|
1603
|
+
| `[Agent] System Prompt` | string | No | The system prompt used for this LLM call (when content_mode permits). Chunked for long prompts. |
|
|
1604
|
+
| `[Agent] System Prompt Length` | number | No | Character length of the system prompt. |
|
|
1605
|
+
| `[Agent] Temperature` | number | No | Temperature parameter used for this LLM call. |
|
|
1606
|
+
| `[Agent] Max Output Tokens` | number | No | Maximum output tokens configured for this LLM call. |
|
|
1607
|
+
| `[Agent] Top P` | number | No | Top-p (nucleus sampling) parameter used for this LLM call. |
|
|
1608
|
+
| `[Agent] Is Streaming` | boolean | No | Whether this response was generated via streaming. |
|
|
1609
|
+
| `[Agent] Prompt ID` | string | No | Identifier for the prompt template or version used. |
|
|
1610
|
+
| `[Agent] Was Copied` | boolean | No | Whether the user copied this AI response content. An implicit positive quality signal. |
|
|
1611
|
+
| `[Agent] Was Cached` | boolean | No | Whether this response was served from a semantic/full-response cache (distinct from token-level prompt caching). |
|
|
1612
|
+
| `[Agent] Model Tier` | string | No | Model tier classification: 'fast' (GPT-4o-mini, Haiku, Flash), 'standard' (GPT-4o, Sonnet, Pro), or 'reasoning' (o1, o3, DeepSeek-R1). Auto-inferred from model name. |
|
|
1613
|
+
| `[Agent] Has Attachments` | boolean | No | Whether this AI response includes generated attachments (images, charts, files). |
|
|
1614
|
+
| `[Agent] Attachment Types` | string[] | No | Distinct attachment types in this AI response. Serialized JSON array. |
|
|
1615
|
+
| `[Agent] Attachment Count` | number | No | Number of attachments generated by the AI in this response. |
|
|
1616
|
+
| `[Agent] Total Attachment Size Bytes` | number | No | Total size of all AI-generated attachments in bytes. |
|
|
1617
|
+
| `[Agent] Attachments` | string | No | Serialized JSON array of AI-generated attachment metadata. |
|
|
1618
|
+
| `[Agent] Message Labels` | string | No | Serialized JSON array of MessageLabel objects attached to this AI response. |
|
|
1619
|
+
| `[Agent] Message Label Map` | string | No | Serialized JSON map of label key to value for quick lookup. |
|
|
1620
|
+
|
|
1621
|
+
### Tool Call Properties
|
|
1622
|
+
|
|
1623
|
+
Event-specific properties for `[Agent] Tool Call` (in addition to common properties above).
|
|
1624
|
+
|
|
1625
|
+
| Property | Type | Required | Description |
|
|
1626
|
+
| ------------------------------- | ------- | -------- | --------------------------------------------------------------------------------------------------------------------------------- |
|
|
1627
|
+
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
|
|
1628
|
+
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
|
|
1629
|
+
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
|
|
1630
|
+
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
|
|
1631
|
+
| `[Agent] Locale` | string | No | User locale (e.g., 'en-US'). |
|
|
1632
|
+
| `[Agent] Span Kind` | string | No | Classification of the span type for OTEL bridge compatibility. |
|
|
1633
|
+
| `[Amplitude] Session Replay ID` | string | No | Links to Amplitude Session Replay (format: device_id/session_id). Enables one-click navigation from AI session to browser replay. |
|
|
1634
|
+
| `[Agent] Invocation ID` | string | Yes | Unique identifier for this tool invocation (UUID). Used to link tool calls to parent messages. |
|
|
1635
|
+
| `[Agent] Tool Name` | string | Yes | Name of the tool/function that was invoked (e.g., 'search_docs', 'web_search'). |
|
|
1636
|
+
| `[Agent] Tool Success` | boolean | Yes | Whether the tool call completed successfully. |
|
|
1637
|
+
| `[Agent] Tool Input` | string | No | Serialized JSON of the tool's input arguments. Only sent when content_mode='full'. |
|
|
1638
|
+
| `[Agent] Tool Output` | string | No | Serialized JSON of the tool's output/return value. Only sent when content_mode='full'. |
|
|
1639
|
+
| `[Agent] Parent Message ID` | string | No | The message_id of the user message that triggered this tool call. Links the tool call into the event graph. |
|
|
1640
|
+
|
|
1641
|
+
### Embedding Properties
|
|
1642
|
+
|
|
1643
|
+
Event-specific properties for `[Agent] Embedding` (in addition to common properties above).
|
|
1644
|
+
|
|
1645
|
+
| Property | Type | Required | Description |
|
|
1646
|
+
| ------------------------------ | ------ | -------- | ------------------------------------------------------------------------------------- |
|
|
1647
|
+
| `[Agent] Component Type` | string | Yes | Type of component that produced this event: 'user_input', 'llm', 'tool', 'embedding'. |
|
|
1648
|
+
| `[Agent] Model Name` | string | Yes | LLM model identifier (e.g., 'gpt-4o', 'claude-sonnet-4-20250514'). |
|
|
1649
|
+
| `[Agent] Provider` | string | Yes | LLM provider name (e.g., 'openai', 'anthropic', 'google', 'mistral', 'bedrock'). |
|
|
1650
|
+
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
|
|
1651
|
+
| `[Agent] Span ID` | string | Yes | Unique identifier for this embedding operation (UUID). |
|
|
1652
|
+
| `[Agent] Input Tokens` | number | No | Number of input tokens processed by the embedding model. |
|
|
1653
|
+
| `[Agent] Embedding Dimensions` | number | No | Dimensionality of the output embedding vector. |
|
|
1654
|
+
| `[Agent] Cost USD` | number | No | Estimated cost in USD for this embedding operation. |
|
|
1655
|
+
|
|
1656
|
+
### Span Properties
|
|
1657
|
+
|
|
1658
|
+
Event-specific properties for `[Agent] Span` (in addition to common properties above).
|
|
1659
|
+
|
|
1660
|
+
| Property | Type | Required | Description |
|
|
1661
|
+
| ------------------------ | ------- | -------- | ------------------------------------------------------------------------------- |
|
|
1662
|
+
| `[Agent] Latency Ms` | number | Yes | Total wall-clock latency in milliseconds for this operation. |
|
|
1663
|
+
| `[Agent] Is Error` | boolean | Yes | Whether this event represents an error condition. |
|
|
1664
|
+
| `[Agent] Error Message` | string | No | Error message text when Is Error is true. |
|
|
1665
|
+
| `[Agent] Span ID` | string | Yes | Unique identifier for this span (UUID). |
|
|
1666
|
+
| `[Agent] Span Name` | string | Yes | Name of the operation (e.g., 'rag_pipeline', 'vector_search', 'rerank'). |
|
|
1667
|
+
| `[Agent] Parent Span ID` | string | No | Span ID of the parent span for nested pipeline steps. |
|
|
1668
|
+
| `[Agent] Input State` | string | No | Serialized JSON of the span's input state. Only sent when content_mode='full'. |
|
|
1669
|
+
| `[Agent] Output State` | string | No | Serialized JSON of the span's output state. Only sent when content_mode='full'. |
|
|
1670
|
+
|
|
1671
|
+
### Session End Properties
|
|
1672
|
+
|
|
1673
|
+
Event-specific properties for `[Agent] Session End` (in addition to common properties above).
|
|
1674
|
+
|
|
1675
|
+
| Property | Type | Required | Description |
|
|
1676
|
+
| -------------------------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1677
|
+
| `[Agent] Enrichments` | string | No | Serialized JSON of SessionEnrichments (topic classifications, rubric scores, outcome, flags). Attached when enrichments are provided at session close. |
|
|
1678
|
+
| `[Agent] Abandonment Turn` | number | No | Turn ID of the last user message that received an AI response before the user left. Low values (e.g., 1) strongly signal first-response dissatisfaction. |
|
|
1679
|
+
| `[Agent] Session Idle Timeout Minutes` | number | No | Custom idle timeout for this session (default 30 min). Tells the server how long to wait before auto-closing. |
|
|
1680
|
+
|
|
1681
|
+
### Session Enrichment Properties
|
|
1682
|
+
|
|
1683
|
+
Event-specific properties for `[Agent] Session Enrichment` (in addition to common properties above).
|
|
1684
|
+
|
|
1685
|
+
| Property | Type | Required | Description |
|
|
1686
|
+
| --------------------- | ------ | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1687
|
+
| `[Agent] Enrichments` | string | Yes | Serialized JSON of SessionEnrichments: topic_classifications, rubrics, overall_outcome, quality_score, sentiment_score, boolean flags, agent chain metadata, and message labels. |
|
|
1688
|
+
|
|
1689
|
+
### Score Properties
|
|
1690
|
+
|
|
1691
|
+
Event-specific properties for `[Agent] Score` (in addition to common properties above).
|
|
1692
|
+
|
|
1693
|
+
| Property | Type | Required | Description |
|
|
1694
|
+
| --------------------------- | ------ | -------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1695
|
+
| `[Agent] Score Name` | string | Yes | Name of the score (e.g., 'user-feedback', 'task_completion', 'accuracy', 'groundedness'). |
|
|
1696
|
+
| `[Agent] Score Value` | number | Yes | Numeric score value. Binary (0/1), continuous (0.0-1.0), or rating scale (1-5). |
|
|
1697
|
+
| `[Agent] Target ID` | string | Yes | The message_id or session_id being scored. |
|
|
1698
|
+
| `[Agent] Target Type` | string | Yes | What is being scored: 'message' or 'session'. |
|
|
1699
|
+
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
|
|
1700
|
+
| `[Agent] Comment` | string | No | Optional text explanation for the score (respects content_mode). |
|
|
1701
|
+
| `[Agent] Taxonomy Version` | string | No | Which taxonomy config version produced this enrichment (from ai_category_config.config_version_id). |
|
|
1702
|
+
| `[Agent] Evaluated At` | number | No | Epoch milliseconds when this enrichment/evaluation was computed. |
|
|
1703
|
+
| `[Agent] Score Label` | string | No | Direction-neutral magnitude label derived from score value. Default 5-tier: very_high (>=0.8), high (>=0.6), moderate (>=0.4), low (>=0.2), very_low (>=0.0). Server-side only. |
|
|
1704
|
+
|
|
1705
|
+
### Server-Side: Session Evaluation Properties
|
|
1706
|
+
|
|
1707
|
+
`[Agent] Session Evaluation` is emitted automatically by the server-side enrichment pipeline — do not send this event from your code.
|
|
1708
|
+
|
|
1709
|
+
| Property | Type | Required | Description |
|
|
1710
|
+
| --------------------------------- | -------- | -------- | -------------------------------------------------------------------------------------------------------------------------- |
|
|
1711
|
+
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
|
|
1712
|
+
| `[Agent] Agent ID` | string | Yes | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
|
|
1713
|
+
| `[Agent] Customer Org ID` | string | Yes | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
|
|
1714
|
+
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
|
|
1715
|
+
| `[Agent] Taxonomy Version` | string | Yes | Which taxonomy config version produced this enrichment (from ai_category_config.config_version_id). |
|
|
1716
|
+
| `[Agent] Evaluated At` | number | Yes | Epoch milliseconds when this enrichment/evaluation was computed. |
|
|
1717
|
+
| `[Agent] Overall Outcome` | string | Yes | Session outcome classification: 'success', 'partial_success', 'failure', 'abandoned', 'response_provided', etc. |
|
|
1718
|
+
| `[Agent] Turn Count` | number | Yes | Number of conversation turns in this session. |
|
|
1719
|
+
| `[Agent] Session Total Tokens` | number | No | Total LLM tokens consumed across all turns in this session. |
|
|
1720
|
+
| `[Agent] Session Avg Latency Ms` | number | No | Average AI response latency in milliseconds across the session. |
|
|
1721
|
+
| `[Agent] Request Complexity` | string | No | Complexity classification of the user's request: 'simple', 'moderate', 'complex', or 'ambiguous'. |
|
|
1722
|
+
| `[Agent] Has Task Failure` | boolean | Yes | Whether the agent failed to complete the user's request. |
|
|
1723
|
+
| `[Agent] Has Negative Feedback` | boolean | Yes | Whether the user expressed dissatisfaction during the session. |
|
|
1724
|
+
| `[Agent] Has Technical Failure` | boolean | Yes | Whether technical errors occurred (tool timeouts, API failures, etc.). |
|
|
1725
|
+
| `[Agent] Has Data Quality Issues` | boolean | Yes | Whether the AI output had data quality problems (wrong data, hallucinations, etc.). |
|
|
1726
|
+
| `[Agent] Models Used` | string[] | No | LLM models used in this session. JSON array of strings. |
|
|
1727
|
+
| `[Agent] Root Agent Name` | string | No | Entry-point agent in multi-agent flows. |
|
|
1728
|
+
| `[Agent] Agent Chain Depth` | number | No | Number of agents in the delegation chain. |
|
|
1729
|
+
| `[Agent] Task Failure Type` | string | No | Specific failure type when has_task_failure is true (e.g., 'wrong_answer', 'unable_to_complete'). |
|
|
1730
|
+
| `[Agent] Technical Error Count` | number | No | Count of technical errors that occurred during the session. |
|
|
1731
|
+
| `[Agent] Error Categories` | string[] | No | Categorized error types (e.g., 'chart_not_found', 'timeout'). JSON array of strings. |
|
|
1732
|
+
| `[Agent] Behavioral Patterns` | string[] | No | Detected behavioral anti-patterns (e.g., 'retry_storm', 'clarification_loop', 'early_abandonment'). JSON array of strings. |
|
|
1733
|
+
| `[Agent] Session Cost USD` | number | No | Total LLM cost in USD for this AI session (aggregated from per-message costs). |
|
|
1734
|
+
| `[Agent] Enrichment Cost USD` | number | No | Cost in USD of running the enrichment pipeline's LLM inference for this session. Distinct from the session's own LLM cost. |
|
|
1735
|
+
| `[Agent] Quality Score` | number | No | Overall quality score (0.0-1.0) computed by the enrichment pipeline for this session. |
|
|
1736
|
+
| `[Agent] Sentiment Score` | number | No | User sentiment score (0.0-1.0) inferred from the conversation by the enrichment pipeline. |
|
|
1737
|
+
| `[Agent] Task Failure Reason` | string | No | Explanation of why the task failed when has_task_failure is true (e.g., 'chart data source unavailable'). |
|
|
1738
|
+
| `[Agent] Agent Chain` | string[] | No | Serialized JSON array of agent IDs representing the delegation chain in multi-agent flows. |
|
|
1739
|
+
| `[Agent] Project ID` | string | No | Amplitude project ID that owns the AI session being evaluated. |
|
|
1740
|
+
| `[Agent] Has User Feedback` | boolean | Yes | Whether the session received explicit user feedback (thumbs up/down, rating). |
|
|
1741
|
+
| `[Agent] User Score` | number | No | Aggregate user feedback score for the session (0.0-1.0). Present only when has_user_feedback is true. |
|
|
1742
|
+
| `[Agent] Agent Version` | string | No | Agent code version (e.g., 'v4.2'). Enables version-over-version quality comparison. |
|
|
1743
|
+
|
|
1744
|
+
### Server-Side: Topic Classification Properties
|
|
1745
|
+
|
|
1746
|
+
`[Agent] Topic Classification` is emitted automatically by the server-side enrichment pipeline — do not send this event from your code.
|
|
1747
|
+
|
|
1748
|
+
| Property | Type | Required | Description |
|
|
1749
|
+
| --------------------------- | -------- | -------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
|
1750
|
+
| `[Agent] Session ID` | string | Yes | Unique session identifier. All events in one conversation share the same session ID. |
|
|
1751
|
+
| `[Agent] Agent ID` | string | Yes | Identifies which AI agent handled the interaction (e.g., 'support-bot', 'houston'). |
|
|
1752
|
+
| `[Agent] Customer Org ID` | string | Yes | Organization ID for multi-tenant platforms. Enables account-level group analytics. |
|
|
1753
|
+
| `[Agent] Evaluation Source` | string | Yes | Source of the evaluation: 'user' (end-user feedback), 'ai' (automated/server pipeline), or 'reviewer' (human expert). |
|
|
1754
|
+
| `[Agent] Taxonomy Version` | string | Yes | Which taxonomy config version produced this enrichment (from ai_category_config.config_version_id). |
|
|
1755
|
+
| `[Agent] Evaluated At` | number | Yes | Epoch milliseconds when this enrichment/evaluation was computed. |
|
|
1756
|
+
| `[Agent] Topic` | string | Yes | Which topic model this classification is for (e.g., 'product_area', 'query_intent', 'error_domain'). |
|
|
1757
|
+
| `[Agent] Selection Mode` | string | Yes | Whether this topic model uses 'single' (MECE) or 'multiple' (multi-label) selection. |
|
|
1758
|
+
| `[Agent] Primary` | string | No | Primary classification value (e.g., 'charts', 'billing_issues'). |
|
|
1759
|
+
| `[Agent] Secondary` | string[] | No | Secondary classifications for multi-label topics. JSON array of strings. |
|
|
1760
|
+
| `[Agent] Subcategories` | string[] | No | Subcategories for finer classification within the primary topic (e.g., 'TREND_ANALYSIS', 'WRONG_EVENT'). JSON array of strings. |
|
|
1761
|
+
|
|
1762
|
+
<!-- END EVENT PROPERTY REFERENCE -->
|
|
1763
|
+
|
|
1764
|
+
## Event JSON Examples
|
|
1765
|
+
|
|
1766
|
+
### [Agent] AI Response
|
|
1767
|
+
|
|
1768
|
+
A realistic example of what gets sent to Amplitude for an AI response:
|
|
1769
|
+
|
|
1770
|
+
```json
|
|
1771
|
+
{
|
|
1772
|
+
"event_type": "[Agent] AI Response",
|
|
1773
|
+
"user_id": "user-42",
|
|
1774
|
+
"event_properties": {
|
|
1775
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1776
|
+
"[Agent] Trace ID": "trace-def456",
|
|
1777
|
+
"[Agent] Turn ID": 2,
|
|
1778
|
+
"[Agent] Message ID": "msg-789xyz",
|
|
1779
|
+
"[Agent] Model Name": "gpt-4o",
|
|
1780
|
+
"[Agent] Provider": "openai",
|
|
1781
|
+
"[Agent] Model Tier": "standard",
|
|
1782
|
+
"[Agent] Latency Ms": 1203,
|
|
1783
|
+
"[Agent] Input Tokens": 150,
|
|
1784
|
+
"[Agent] Output Tokens": 847,
|
|
1785
|
+
"[Agent] Total Tokens": 997,
|
|
1786
|
+
"[Agent] Cost USD": 0.0042,
|
|
1787
|
+
"[Agent] Is Error": false,
|
|
1788
|
+
"[Agent] Finish Reason": "stop",
|
|
1789
|
+
"[Agent] Is Streaming": false,
|
|
1790
|
+
"[Agent] Component Type": "llm",
|
|
1791
|
+
"[Agent] Agent ID": "support-bot",
|
|
1792
|
+
"[Agent] Env": "production",
|
|
1793
|
+
"[Agent] SDK Version": "0.1.0",
|
|
1794
|
+
"[Agent] Runtime": "node"
|
|
1795
|
+
}
|
|
1796
|
+
}
|
|
1797
|
+
```
|
|
1798
|
+
|
|
1799
|
+
### [Agent] User Message
|
|
1800
|
+
|
|
1801
|
+
```json
|
|
1802
|
+
{
|
|
1803
|
+
"event_type": "[Agent] User Message",
|
|
1804
|
+
"user_id": "user-42",
|
|
1805
|
+
"event_properties": {
|
|
1806
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1807
|
+
"[Agent] Turn ID": 1,
|
|
1808
|
+
"[Agent] Message ID": "msg-123abc",
|
|
1809
|
+
"[Agent] Component Type": "user_input",
|
|
1810
|
+
"[Agent] Agent ID": "support-bot",
|
|
1811
|
+
"[Agent] Env": "production",
|
|
1812
|
+
"[Agent] SDK Version": "0.1.0",
|
|
1813
|
+
"[Agent] Runtime": "node",
|
|
1814
|
+
"$llm_message": {
|
|
1815
|
+
"text": "How do I reset my password?"
|
|
1816
|
+
}
|
|
1817
|
+
}
|
|
1818
|
+
}
|
|
1819
|
+
```
|
|
1820
|
+
|
|
1821
|
+
### [Agent] Tool Call
|
|
1822
|
+
|
|
1823
|
+
```json
|
|
1824
|
+
{
|
|
1825
|
+
"event_type": "[Agent] Tool Call",
|
|
1826
|
+
"user_id": "user-42",
|
|
1827
|
+
"event_properties": {
|
|
1828
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1829
|
+
"[Agent] Turn ID": 3,
|
|
1830
|
+
"[Agent] Invocation ID": "inv-456def",
|
|
1831
|
+
"[Agent] Tool Name": "search_knowledge_base",
|
|
1832
|
+
"[Agent] Tool Success": true,
|
|
1833
|
+
"[Agent] Is Error": false,
|
|
1834
|
+
"[Agent] Latency Ms": 340,
|
|
1835
|
+
"[Agent] Component Type": "tool",
|
|
1836
|
+
"[Agent] Agent ID": "support-bot",
|
|
1837
|
+
"[Agent] Tool Input": "{\"query\":\"password reset instructions\"}",
|
|
1838
|
+
"[Agent] Tool Output": "{\"results\":[{\"title\":\"Password Reset Guide\"}]}",
|
|
1839
|
+
"[Agent] SDK Version": "0.1.0",
|
|
1840
|
+
"[Agent] Runtime": "node"
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
```
|
|
1844
|
+
|
|
1845
|
+
### [Agent] Score
|
|
1846
|
+
|
|
1847
|
+
```json
|
|
1848
|
+
{
|
|
1849
|
+
"event_type": "[Agent] Score",
|
|
1850
|
+
"user_id": "user-42",
|
|
1851
|
+
"event_properties": {
|
|
1852
|
+
"[Agent] Score Name": "thumbs-up",
|
|
1853
|
+
"[Agent] Score Value": 1,
|
|
1854
|
+
"[Agent] Target ID": "msg-789xyz",
|
|
1855
|
+
"[Agent] Target Type": "message",
|
|
1856
|
+
"[Agent] Evaluation Source": "user",
|
|
1857
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1858
|
+
"[Agent] Agent ID": "support-bot",
|
|
1859
|
+
"[Agent] SDK Version": "0.1.0",
|
|
1860
|
+
"[Agent] Runtime": "node"
|
|
1861
|
+
}
|
|
1862
|
+
}
|
|
1863
|
+
```
|
|
1864
|
+
|
|
1865
|
+
## Sending Events Without the SDK
|
|
1866
|
+
|
|
1867
|
+
The `[Agent]` event schema is not tied to this SDK. If your stack doesn't have an Amplitude AI SDK, you can send the same events directly via Amplitude's ingestion APIs.
|
|
1868
|
+
|
|
1869
|
+
### What the SDK handles for you
|
|
1870
|
+
|
|
1871
|
+
When you use this SDK, the following are managed automatically. If you send events directly, you are responsible for these:
|
|
1872
|
+
|
|
1873
|
+
| Concern | SDK behavior | DIY equivalent |
|
|
1874
|
+
| ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
1875
|
+
| **Session ID** | Generated once per `session()` and propagated to every event | Generate a UUID per conversation and include it as `[Agent] Session ID` on every event |
|
|
1876
|
+
| **Deduplication** | Automatic `insert_id` on each event | Set a unique `insert_id` per event to prevent duplicates on retry |
|
|
1877
|
+
| **Property prefixing** | All properties are prefixed with `[Agent]` | You must include the `[Agent] ` prefix in every property name |
|
|
1878
|
+
| **Cost / token calculation** | Auto-computed from model and token counts | Compute and send `[Agent] Cost USD`, `[Agent] Input Tokens`, etc. yourself |
|
|
1879
|
+
| **Server-side enrichment** | `[Agent] Session Evaluation`, `[Agent] Topic Classification`, and `[Agent] Score` events are emitted automatically by the enrichment pipeline after `[Agent] Session End` | These fire automatically — you do **not** need to send them. Just send the SDK-level events and close the session with `[Agent] Session End`. |
|
|
1880
|
+
|
|
1881
|
+
### Ingestion methods
|
|
1882
|
+
|
|
1883
|
+
| Method | Best for | Docs |
|
|
1884
|
+
| -------------------------- | ------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
|
1885
|
+
| **HTTP V2 API** | Real-time, low-to-medium volume | [HTTP V2 API docs](https://www.docs.developers.amplitude.com/analytics/apis/http-v2-api/) |
|
|
1886
|
+
| **Batch Event Upload API** | High volume, backfills | [Batch API docs](https://www.docs.developers.amplitude.com/analytics/apis/batch-event-upload-api/) |
|
|
1887
|
+
| **Amazon S3 Import** | Bulk historical import, warehouse-first workflows | [S3 Import docs](https://amplitude.com/docs/data/source-catalog/amazon-s3) |
|
|
1888
|
+
|
|
1889
|
+
### Minimal HTTP API example
|
|
1890
|
+
|
|
1891
|
+
```bash
|
|
1892
|
+
curl -X POST https://api2.amplitude.com/2/httpapi \
|
|
1893
|
+
-H 'Content-Type: application/json' \
|
|
1894
|
+
-d '{
|
|
1895
|
+
"api_key": "YOUR_API_KEY",
|
|
1896
|
+
"events": [
|
|
1897
|
+
{
|
|
1898
|
+
"event_type": "[Agent] User Message",
|
|
1899
|
+
"user_id": "user-42",
|
|
1900
|
+
"insert_id": "evt-unique-id-1",
|
|
1901
|
+
"event_properties": {
|
|
1902
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1903
|
+
"[Agent] Trace ID": "trace-def456",
|
|
1904
|
+
"[Agent] Turn ID": 1,
|
|
1905
|
+
"[Agent] Agent ID": "support-bot",
|
|
1906
|
+
"[Agent] Message ID": "msg-001"
|
|
1907
|
+
}
|
|
1908
|
+
},
|
|
1909
|
+
{
|
|
1910
|
+
"event_type": "[Agent] AI Response",
|
|
1911
|
+
"user_id": "user-42",
|
|
1912
|
+
"insert_id": "evt-unique-id-2",
|
|
1913
|
+
"event_properties": {
|
|
1914
|
+
"[Agent] Session ID": "sess-abc123",
|
|
1915
|
+
"[Agent] Trace ID": "trace-def456",
|
|
1916
|
+
"[Agent] Turn ID": 1,
|
|
1917
|
+
"[Agent] Message ID": "msg-002",
|
|
1918
|
+
"[Agent] Agent ID": "support-bot",
|
|
1919
|
+
"[Agent] Model Name": "gpt-4o",
|
|
1920
|
+
"[Agent] Provider": "openai",
|
|
1921
|
+
"[Agent] Latency Ms": 1203,
|
|
1922
|
+
"[Agent] Input Tokens": 150,
|
|
1923
|
+
"[Agent] Output Tokens": 420,
|
|
1924
|
+
"[Agent] Cost USD": 0.0042
|
|
1925
|
+
}
|
|
1926
|
+
}
|
|
1927
|
+
]
|
|
1928
|
+
}'
|
|
1929
|
+
```
|
|
1930
|
+
|
|
1931
|
+
Refer to the [Event Schema](#event-schema) and [Event Property Reference](#event-property-reference) tables above for required and optional properties per event type.
|
|
1932
|
+
|
|
1933
|
+
## Register Event Schema in Your Data Catalog
|
|
1934
|
+
|
|
1935
|
+
Amplitude's [Data Catalog](https://amplitude.com/docs/data/data-catalog) documents events and properties with descriptions, types, and required flags. The `@amplitude/ai` package includes a tool to generate all the Taxonomy API calls for you.
|
|
1936
|
+
|
|
1937
|
+
### Prerequisites
|
|
1938
|
+
|
|
1939
|
+
- **Amplitude Enterprise plan** (Taxonomy API access)
|
|
1940
|
+
- **Project API key and Secret key** from Settings > Projects in your Amplitude org
|
|
1941
|
+
|
|
1942
|
+
### Option A: Generate curl commands (JS-native, no dependencies)
|
|
1943
|
+
|
|
1944
|
+
The bundled CLI reads `data/agent_event_catalog.json` and prints executable curl commands — it makes **no network requests** itself.
|
|
1945
|
+
|
|
1946
|
+
```bash
|
|
1947
|
+
# Preview the curl commands (uses placeholder keys)
|
|
1948
|
+
npx amplitude-ai-register-catalog
|
|
1949
|
+
|
|
1950
|
+
# Generate with your real keys
|
|
1951
|
+
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET
|
|
1952
|
+
|
|
1953
|
+
# Pipe to bash to execute immediately
|
|
1954
|
+
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET | bash
|
|
1955
|
+
|
|
1956
|
+
# EU data residency
|
|
1957
|
+
npx amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET --eu | bash
|
|
1958
|
+
```
|
|
1959
|
+
|
|
1960
|
+
### Option B: Python CLI (direct execution)
|
|
1961
|
+
|
|
1962
|
+
If you have Python available, the `amplitude-ai` package provides a CLI that calls the Taxonomy API directly with retry logic and a progress summary:
|
|
1963
|
+
|
|
1964
|
+
```bash
|
|
1965
|
+
pip install amplitude-ai
|
|
1966
|
+
amplitude-ai-register-catalog --api-key YOUR_KEY --secret-key YOUR_SECRET
|
|
1967
|
+
```
|
|
1968
|
+
|
|
1969
|
+
### What gets registered
|
|
1970
|
+
|
|
1971
|
+
All 10 `[Agent]` event types and their properties (see [Event Property Reference](#event-property-reference) above), organized under the "Agent Analytics" category. The commands are **idempotent** — safe to re-run. They create missing events/properties and update existing ones.
|
|
1972
|
+
|
|
1973
|
+
## Utilities and Type Exports
|
|
1974
|
+
|
|
1975
|
+
### Token and cost utilities
|
|
1976
|
+
|
|
1977
|
+
- **`calculateCost()`** — Returns cost in USD when `@pydantic/genai-prices` is installed; otherwise returns `0` (never `null`).
|
|
1978
|
+
- **`countTokens(text, model?)`** — Uses tiktoken when available. For unknown models, tries `o200k_base` encoding before falling back to `cl100k_base` (matching the Python SDK).
|
|
1979
|
+
- **`estimateTokens(text)`** — Heuristic fallback: `ceil(chars/3.5 + words*0.1)` (matching the Python SDK).
|
|
1980
|
+
- **`stripProviderPrefix(modelName)`** — Splits on `:` (e.g., `openai:gpt-4o` → `gpt-4o`). Use for normalizing model IDs before cost lookup. Import from `@amplitude/ai/internals`.
|
|
1981
|
+
|
|
1982
|
+
### Shared types
|
|
1983
|
+
|
|
1984
|
+
The package exports structural interfaces for provider shapes from `@amplitude/ai` and `@amplitude/ai/types`: `ChatCompletionParams`, `ChatCompletionResponse`, `AnthropicParams`, `AnthropicResponse`, `BedrockConverseParams`, `BedrockConverseResponse`, `MistralChatParams`, `MistralChatResponse`, `TrackFn`, `TrackCallOptions`, and related types. Use these for typing provider integrations without depending on the underlying SDK types.
|
|
1985
|
+
|
|
1986
|
+
## Constants
|
|
1987
|
+
|
|
1988
|
+
All `PROP_*` and `EVENT_*` constants are exported for advanced use:
|
|
1989
|
+
|
|
1990
|
+
```typescript
|
|
1991
|
+
import {
|
|
1992
|
+
EVENT_AI_RESPONSE,
|
|
1993
|
+
EVENT_EMBEDDING,
|
|
1994
|
+
EVENT_SCORE,
|
|
1995
|
+
EVENT_SESSION_END,
|
|
1996
|
+
EVENT_SESSION_ENRICHMENT,
|
|
1997
|
+
EVENT_SPAN,
|
|
1998
|
+
EVENT_TOOL_CALL,
|
|
1999
|
+
EVENT_USER_MESSAGE,
|
|
2000
|
+
PROP_MODEL_NAME,
|
|
2001
|
+
PROP_SESSION_ID,
|
|
2002
|
+
PROP_TRACE_ID,
|
|
2003
|
+
// ... etc
|
|
2004
|
+
} from '@amplitude/ai';
|
|
2005
|
+
```
|
|
2006
|
+
|
|
2007
|
+
See `src/core/tracking.ts` and `src/core/constants.ts` for the full list.
|
|
2008
|
+
|
|
2009
|
+
## API Reference
|
|
2010
|
+
|
|
2011
|
+
- [Reference Index](docs/api/reference.md)
|
|
2012
|
+
- [Client API](docs/api/client.md)
|
|
2013
|
+
- [Configuration API](docs/api/config.md)
|
|
2014
|
+
- [Integrations API](docs/api/integrations.md)
|
|
2015
|
+
- [Testing API](docs/api/testing.md)
|
|
2016
|
+
- [Constants API](docs/api/constants.md)
|
|
2017
|
+
- [Event Schema](docs/api/event-schema.md)
|
|
2018
|
+
- [Exceptions API](docs/api/exceptions.md)
|
|
2019
|
+
|
|
2020
|
+
## For AI Coding Agents
|
|
2021
|
+
|
|
2022
|
+
This SDK is designed to be discovered and used by AI coding agents (Cursor, Copilot, Claude Code, etc.). The following files ship with the package to help agents understand and integrate the SDK without reading the full README:
|
|
2023
|
+
|
|
2024
|
+
| File | Purpose |
|
|
2025
|
+
| ------------------------------------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
2026
|
+
| `AGENTS.md` | Machine-readable decision tree, canonical patterns, MCP surface, gotchas, and CLI reference |
|
|
2027
|
+
| `llms.txt` | Compact discovery file listing tools, resources, and event names |
|
|
2028
|
+
| `llms-full.txt` | Extended reference with full API signatures, provider coverage matrix, and common error resolutions |
|
|
2029
|
+
| `mcp.schema.json` | Structured JSON describing the MCP server's tools, resources, and prompt |
|
|
2030
|
+
| `.cursor/skills/instrument-with-amplitude-ai/SKILL.md` | Cursor skill that guides agents through instrumenting a project step by step |
|
|
2031
|
+
|
|
2032
|
+
Run `amplitude-ai mcp` to start the MCP server. Agents can call tools like `validate_file` to scan source code for uninstrumented LLM calls, or `suggest_instrumentation` for context-aware setup guidance based on the detected framework and provider.
|
|
2033
|
+
|
|
2034
|
+
## For Python SDK Migrators
|
|
2035
|
+
|
|
2036
|
+
If you're moving from `amplitude_ai` (Python) to `@amplitude/ai` (TypeScript/Node), the core event model is the same, but ergonomics differ to match the runtime:
|
|
2037
|
+
|
|
2038
|
+
| Area | Python (`amplitude_ai`) | TypeScript (`@amplitude/ai`) |
|
|
2039
|
+
| ----------------------- | ------------------------------------------- | ------------------------------------------------------------------------------ |
|
|
2040
|
+
| Session scope | `with session as s:` | `await session.run(async (s) => { ... })` |
|
|
2041
|
+
| Tool/observe wrappers | `@tool`, `@observe` decorators | `tool()`, `observe()` HOFs |
|
|
2042
|
+
| Context propagation | `contextvars` | `AsyncLocalStorage` |
|
|
2043
|
+
| Tool input schema | Optional auto-schema from Python type hints | Explicit `inputSchema` object (recommended: define with Zod, pass JSON Schema) |
|
|
2044
|
+
| Sync behavior | Native sync + async wrappers | Wrappers return async (`Promise<T>`) |
|
|
2045
|
+
| Middleware | Starlette/FastAPI middleware | Express-compatible middleware |
|
|
2046
|
+
| Bootstrap/preload | `sitecustomize.py` + `PYTHONPATH` patterns | `NODE_OPTIONS=--import` preload patterns |
|
|
2047
|
+
| Provider patching model | Python class replacement | Prototype patching + Proxy fallback for lazy getters |
|
|
2048
|
+
|
|
2049
|
+
Features that do not map 1:1 because of platform/runtime constraints:
|
|
2050
|
+
|
|
2051
|
+
- Auto-generated tool schemas from runtime type introspection
|
|
2052
|
+
- Python-style per-call keyword overrides (for example `amplitude_user_id=...`)
|
|
2053
|
+
- Interrupting synchronous tool execution with Python threading primitives
|
|
2054
|
+
- CrewAI integration (Python-only; TS package throws a clear error)
|
|
2055
|
+
|
|
2056
|
+
### Python → TypeScript cheat sheet
|
|
2057
|
+
|
|
2058
|
+
```python
|
|
2059
|
+
# Python
|
|
2060
|
+
from amplitude_ai import AmplitudeAI, tool, observe
|
|
2061
|
+
|
|
2062
|
+
ai = AmplitudeAI(api_key="xxx")
|
|
2063
|
+
agent = ai.agent("my-agent", user_id="u1")
|
|
2064
|
+
|
|
2065
|
+
with agent.session(user_id="u1") as s:
|
|
2066
|
+
s.track_user_message("Hello")
|
|
2067
|
+
s.track_ai_message("Hi!", model="gpt-4", provider="openai", latency_ms=100)
|
|
2068
|
+
|
|
2069
|
+
@tool(name="search")
|
|
2070
|
+
def search(query: str) -> str:
|
|
2071
|
+
return db.search(query)
|
|
2072
|
+
```
|
|
2073
|
+
|
|
2074
|
+
```typescript
|
|
2075
|
+
// TypeScript
|
|
2076
|
+
import { AmplitudeAI, tool } from '@amplitude/ai';
|
|
2077
|
+
|
|
2078
|
+
const ai = new AmplitudeAI({ apiKey: 'xxx' });
|
|
2079
|
+
const agent = ai.agent('my-agent', { userId: 'u1' });
|
|
2080
|
+
|
|
2081
|
+
const session = agent.session({ userId: 'u1' });
|
|
2082
|
+
await session.run(async (s) => {
|
|
2083
|
+
s.trackUserMessage('Hello');
|
|
2084
|
+
s.trackAiMessage('Hi!', 'gpt-4', 'openai', 100);
|
|
2085
|
+
});
|
|
2086
|
+
|
|
2087
|
+
const search = tool(async (args: { query: string }) => db.search(args.query), {
|
|
2088
|
+
name: 'search',
|
|
2089
|
+
});
|
|
2090
|
+
```
|
|
2091
|
+
|
|
2092
|
+
## Need Help?
|
|
2093
|
+
|
|
2094
|
+
- **Bug reports and feature requests**: [Open an issue](https://github.com/amplitude/Amplitude-AI-Node/issues)
|
|
2095
|
+
- **General questions**: [Amplitude Support](https://help.amplitude.com)
|
|
2096
|
+
- **Python SDK**: Looking for the Python version? See [amplitude-ai on PyPI](https://pypi.org/project/amplitude-ai/)
|
|
2097
|
+
|
|
2098
|
+
## Contributing
|
|
2099
|
+
|
|
2100
|
+
Contributions are welcome! Please open an issue first to discuss what you'd like to change, then submit a pull request.
|
|
2101
|
+
|
|
2102
|
+
1. Fork the repository
|
|
2103
|
+
2. Create your branch (`git checkout -b my-feature`)
|
|
2104
|
+
3. Install dependencies (`pnpm install`)
|
|
2105
|
+
4. Make your changes and add tests
|
|
2106
|
+
5. Ensure all tests pass (`pnpm run test:coverage`) and TypeScript compiles (`pnpm run test:typescript`)
|
|
2107
|
+
6. Submit a pull request
|
|
2108
|
+
|
|
2109
|
+
## License
|
|
2110
|
+
|
|
2111
|
+
[MIT](LICENSE)
|