UNPKG

axiom

Version:

Axiom AI SDK provides - an API to wrap your AI calls with observability instrumentation. - offline evals

1 lines 14.6 kB
{"version":3,"sources":["../src/feedback.ts","../src/schema.ts","../src/util/errors.ts","../src/util/feedback.ts"],"sourcesContent":["import { SCHEMA_URL } from './schema';\nimport { errorToString } from './util/errors';\nimport { getSuffix } from './util/feedback';\n\ntype FeedbackInputBase = {\n readonly name: string;\n readonly message?: string;\n readonly category?: string;\n readonly metadata?: Record<string, unknown>;\n};\n\ntype FeedbackCoreNumber = {\n readonly kind: 'number';\n readonly value: number;\n};\n\ntype FeedbackCoreThumb = {\n readonly kind: 'thumb';\n readonly value: -1 | 1;\n};\n\ntype FeedbackCoreBoolean = {\n readonly kind: 'boolean';\n readonly value: boolean;\n};\n\ntype FeedbackCoreText = {\n readonly kind: 'text';\n readonly value: string;\n};\n\ntype FeedbackCoreSignal = {\n readonly kind: 'signal';\n readonly value: null;\n};\n\n/** Feedback with a number value (e.g., similarity, 0-1). */\ntype FeedbackInputNumber = FeedbackInputBase & FeedbackCoreNumber;\n\n/** Feedback with a thumbs up (+1) or thumbs down (-1) value. */\ntype FeedbackInputThumb = FeedbackInputBase & FeedbackCoreThumb;\n\n/** Feedback with a boolean value (true/false). */\ntype FeedbackInputBoolean = FeedbackInputBase & FeedbackCoreBoolean;\n\n/** Feedback with a free-form text value. */\ntype FeedbackInputText = FeedbackInputBase & FeedbackCoreText;\n\n/** Feedback without a value, used to signal an event occurred. */\ntype FeedbackInputSignal = FeedbackInputBase & FeedbackCoreSignal;\n\n/** Union of all feedback input types. Discriminated on `kind`. */\ntype FeedbackInput =\n | FeedbackInputNumber\n | FeedbackInputThumb\n | FeedbackInputBoolean\n | FeedbackInputText\n | FeedbackInputSignal;\n\n/** Links that associate feedback with a trace and other context. */\ntype FeedbackLinks = {\n /** The trace ID to associate this feedback with. */\n readonly traceId: string;\n /** The capability (feature/component) this feedback relates to. */\n readonly capability: string;\n /** Optional step within the capability. */\n readonly step?: string;\n /** Optional span ID for more granular linking. */\n readonly spanId?: string;\n /** Optional conversation ID for conversational contexts. */\n readonly conversationId?: string;\n /** Optional user ID of the person providing feedback. */\n readonly userId?: string;\n};\n\ntype FeedbackLinksSerialized = {\n readonly trace_id: string;\n readonly capability: string;\n readonly step?: string;\n readonly span_id?: string;\n readonly conversation_id?: string;\n readonly userId?: string;\n};\n\ntype FeedbackEventBase = {\n readonly schemaUrl: string;\n readonly id: string;\n readonly name: string;\n readonly message?: string;\n readonly category?: string;\n readonly metadata?: Record<string, unknown>;\n readonly links: FeedbackLinksSerialized;\n readonly event: 'feedback';\n};\n\ntype FeedbackEventPayloadNumber = FeedbackEventBase & FeedbackCoreNumber;\ntype FeedbackEventPayloadThumb = FeedbackEventBase & FeedbackCoreThumb;\ntype FeedbackEventPayloadBoolean = FeedbackEventBase & FeedbackCoreBoolean;\ntype FeedbackEventPayloadText = FeedbackEventBase & FeedbackCoreText;\ntype FeedbackEventPayloadSignal = FeedbackEventBase & FeedbackCoreSignal;\n\ntype FeedbackEventPayload =\n | FeedbackEventPayloadNumber\n | FeedbackEventPayloadThumb\n | FeedbackEventPayloadBoolean\n | FeedbackEventPayloadText\n | FeedbackEventPayloadSignal;\n\ntype FeedbackEventStoredBase = FeedbackEventBase & {\n readonly _time: string;\n};\n\ntype FeedbackEventNumber = FeedbackEventStoredBase & FeedbackCoreNumber;\ntype FeedbackEventThumb = FeedbackEventStoredBase & FeedbackCoreThumb;\ntype FeedbackEventBoolean = FeedbackEventStoredBase & FeedbackCoreBoolean;\ntype FeedbackEventText = FeedbackEventStoredBase & FeedbackCoreText;\ntype FeedbackEventSignal = FeedbackEventStoredBase & FeedbackCoreSignal;\n\n/** Union of all feedback event types as returned from Axiom queries. Discriminated on `kind`. */\ntype FeedbackEvent =\n | FeedbackEventNumber\n | FeedbackEventThumb\n | FeedbackEventBoolean\n | FeedbackEventText\n | FeedbackEventSignal;\n\n/** Parameters for creating a number feedback (excludes `kind`). */\ntype FeedbackParamsNumber = Omit<FeedbackInputNumber, 'kind'>;\n\n/** Parameters for creating a thumb feedback (excludes `kind`). */\n// type FeedbackParamsThumb = Omit<FeedbackInputThumb, 'kind'>;\n\n/** Parameters for creating a boolean feedback (excludes `kind`). */\ntype FeedbackParamsBoolean = Omit<FeedbackInputBoolean, 'kind'>;\n\n/** Parameters for creating a text feedback (excludes `kind`). */\ntype FeedbackParamsText = Omit<FeedbackInputText, 'kind'>;\n\n/** Parameters for creating a signal feedback (excludes `kind` and `value`). */\ntype FeedbackParamsSignal = Omit<FeedbackInputSignal, 'kind' | 'value'>;\n\n/** Base parameters shared by all feedback types (name, message, category, metadata). */\ntype FeedbackParamsBase = FeedbackInputBase;\n\nconst withKind = <T extends FeedbackInput>(input: Omit<T, 'kind'>, kind: T['kind']): T =>\n ({\n ...input,\n kind,\n }) as T;\n\n/** Configuration for connecting to the Axiom feedback API. */\ntype FeedbackConfig = {\n /** Axiom API token with ingest permissions. */\n readonly token: string;\n /** Axiom dataset to send feedback to. */\n readonly dataset: string;\n /** Optional custom Axiom API URL. Defaults to https://api.axiom.co. */\n readonly url?: string;\n};\n\ntype FeedbackErrorContext = {\n readonly links: FeedbackLinks;\n readonly feedback: FeedbackInput;\n};\n\ntype FeedbackSettings = {\n readonly onError?: (error: Error, context: FeedbackErrorContext) => void;\n};\n\n/** Function signature for sending feedback. */\ntype SendFeedbackFn = (links: FeedbackLinks, feedback: FeedbackInput) => Promise<void>;\n\n/** Client for sending feedback to Axiom. */\ntype FeedbackClient = {\n /** Sends feedback associated with the given links. */\n readonly sendFeedback: SendFeedbackFn;\n};\n\n/**\n * Creates a feedback client for sending user feedback to Axiom.\n *\n * @example\n * ```ts\n * const client = createFeedbackClient({ token: 'xaat-...', dataset: 'feedback' });\n * void client.sendFeedback(\n * { traceId: '...', capability: 'support-agent' },\n * Feedback.thumbUp({ name: 'response-quality' })\n * );\n * ```\n */\nconst createFeedbackClient = (\n config: FeedbackConfig,\n settings?: FeedbackSettings,\n): FeedbackClient => {\n const baseUrl = config.url ?? 'https://api.axiom.co';\n const url = `${baseUrl}${getSuffix(baseUrl, config.dataset)}`;\n\n const sendFeedback: SendFeedbackFn = async (\n links: FeedbackLinks,\n feedback: FeedbackInput,\n ): Promise<void> => {\n const { metadata, ...feedbackFields } = feedback;\n\n const { traceId, spanId, conversationId, ...restLinks } = links;\n const serializedLinks = {\n trace_id: traceId,\n ...restLinks,\n ...(spanId !== undefined && { span_id: spanId }),\n ...(conversationId !== undefined && { conversation_id: conversationId }),\n };\n\n const payload: FeedbackEventPayload = {\n schemaUrl: SCHEMA_URL,\n id: crypto.randomUUID(),\n ...feedbackFields,\n links: serializedLinks,\n event: 'feedback',\n ...(metadata !== undefined && { metadata }),\n };\n\n try {\n // TODO: maybe use axiom-js for this?\n const response = await fetch(url, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n Authorization: `Bearer ${config.token}`,\n },\n body: JSON.stringify(payload),\n });\n\n if (!response.ok) {\n const text = await response.text();\n settings?.onError?.(\n new Error(`Failed to send feedback to Axiom: ${response.status} ${text}`),\n { links, feedback },\n );\n }\n } catch (error) {\n const e = error instanceof Error ? error : new Error(errorToString(error));\n if (settings?.onError) {\n settings.onError(e, { links, feedback });\n } else {\n console.error(e);\n }\n }\n };\n\n return { sendFeedback };\n};\n\nconst thumbFeedback = ({\n name,\n value,\n message,\n category,\n metadata,\n}: FeedbackParamsBase & { readonly value: 'up' | 'down' }): FeedbackInputThumb =>\n withKind({ name, value: value === 'up' ? 1 : -1, message, category, metadata }, 'thumb');\n\nconst thumbUpFeedback = (input: FeedbackParamsBase): FeedbackInputThumb =>\n thumbFeedback({ ...input, value: 'up' });\n\nconst thumbDownFeedback = (input: FeedbackParamsBase): FeedbackInputThumb =>\n thumbFeedback({ ...input, value: 'down' });\n\nconst enumFeedback = <T extends string>(\n input: FeedbackParamsBase & { readonly value: T },\n): FeedbackInputText => withKind(input, 'text');\n\nconst numberFeedback = (input: FeedbackParamsNumber): FeedbackInputNumber =>\n withKind(input, 'number');\n\nconst boolFeedback = (input: FeedbackParamsBoolean): FeedbackInputBoolean =>\n withKind(input, 'boolean');\n\nconst textFeedback = (input: FeedbackParamsText): FeedbackInputText => withKind(input, 'text');\n\nconst signalFeedback = (input: FeedbackParamsSignal): FeedbackInputSignal =>\n withKind({ ...input, value: null }, 'signal');\n\n/**\n * Helper functions for creating feedback input objects.\n *\n * @example\n * ```ts\n * Feedback.thumbUp({ name: 'response-quality' })\n * Feedback.number({ name: 'rating', value: 4 })\n * Feedback.signal({ name: 'completed' })\n * ```\n */\nconst Feedback = {\n /** Creates a signal feedback (no value, just indicates an event occurred). */\n signal: signalFeedback,\n /** Creates a number feedback with a number value. */\n number: numberFeedback,\n /** Creates a boolean feedback with a true/false value. */\n bool: boolFeedback,\n /** Creates a text feedback with a free-form string value. */\n text: textFeedback,\n /** Creates a text feedback from a string enum value. */\n enum: enumFeedback,\n /** Creates a thumb feedback with 'up' or 'down' value. */\n thumb: thumbFeedback,\n /** Creates a thumbs up (+1) feedback. */\n thumbUp: thumbUpFeedback,\n /** Creates a thumbs down (-1) feedback. */\n thumbDown: thumbDownFeedback,\n};\n\nexport type {\n FeedbackInput,\n FeedbackEvent,\n FeedbackClient,\n FeedbackConfig,\n FeedbackErrorContext,\n FeedbackLinks,\n FeedbackSettings,\n SendFeedbackFn,\n};\n\nexport { createFeedbackClient, Feedback };\n","export const SCHEMA_VERSION = '0.0.2';\nexport const SCHEMA_BASE_URL = 'https://axiom.co/ai/schemas/';\nexport const SCHEMA_URL = `${SCHEMA_BASE_URL}${SCHEMA_VERSION}`;\n","export class AxiomCLIError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'AxiomCLIError';\n }\n}\n\nfunction getCircularReplacer() {\n const seen = new WeakSet();\n return (_k: string, v: any) => {\n if (typeof v === 'object' && v !== null) {\n if (seen.has(v)) return '[Circular]';\n seen.add(v);\n }\n return v;\n };\n}\n\nfunction safeJson(x: any) {\n try {\n return JSON.stringify(x, getCircularReplacer());\n } catch {\n return String(x);\n }\n}\n\nexport function errorToString(err: unknown) {\n try {\n if (typeof err === 'string') return err;\n\n if (err instanceof Error) {\n return err.stack ?? err.message;\n }\n\n if (typeof err === 'object' && err !== null) {\n const msg = (err as any).message;\n const json = safeJson(err);\n return msg ? `${msg} (${json})` : json;\n }\n\n return String(err);\n } catch {\n return '[unserializable error]';\n }\n}\n","export function getSuffix(baseUrl: string, dataset: string) {\n if (baseUrl.includes('edge.axiom')) {\n return `/v1/ingest/${dataset}`;\n }\n\n return `/v1/datasets/${dataset}/ingest`;\n}\n"],"mappings":"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","names":[]}