{"version":3,"file":"embeddings.cjs","sources":["../../../src/memory/embeddings.ts"],"sourcesContent":["/**\n * Provider-agnostic embedding client for autonomous memory.\n *\n * Factory pattern mirrors host's `getSharedSummaryBedrockClient()` in\n * `api/server/controllers/agents/client.js` — lazy init, shared instance,\n * testable reset. Same AWS credential chain as host's existing Bedrock\n * calls, so no new IAM policy or secret.\n */\nimport {\n  BedrockRuntimeClient,\n  InvokeModelCommand,\n} from '@aws-sdk/client-bedrock-runtime';\nimport {\n  DEFAULT_MEMORY_DIMENSIONS,\n  DEFAULT_MEMORY_MODEL,\n  DEFAULT_MEMORY_PROVIDER,\n} from './constants';\n\nexport type EmbeddingProviderKind = 'bedrock' | 'openai';\n\nexport interface EmbeddingProvider {\n  readonly kind: EmbeddingProviderKind;\n  readonly model: string;\n  readonly dimensions: number;\n  embed(text: string): Promise<number[]>;\n  embedBatch(texts: string[]): Promise<number[][]>;\n}\n\ninterface ResolvedEmbedderConfig {\n  provider: EmbeddingProviderKind;\n  model: string;\n  dimensions: number;\n}\n\n/**\n * Read embedder config from environment. Called once per factory invocation;\n * fresh reads make testing override-friendly via {@link resetMemoryEmbedder}.\n */\nfunction resolveConfig(): ResolvedEmbedderConfig {\n  const rawProvider = (\n    process.env.MEMORY_EMBEDDINGS_PROVIDER ?? DEFAULT_MEMORY_PROVIDER\n  ).toLowerCase();\n  if (rawProvider !== 'bedrock' && rawProvider !== 'openai') {\n    throw new Error(\n      `Unsupported MEMORY_EMBEDDINGS_PROVIDER: \"${rawProvider}\". Supported: bedrock, openai.`\n    );\n  }\n  const model = process.env.MEMORY_EMBEDDINGS_MODEL ?? DEFAULT_MEMORY_MODEL;\n  const dimensions =\n    Number(process.env.MEMORY_EMBEDDINGS_DIMENSIONS) ||\n    DEFAULT_MEMORY_DIMENSIONS;\n  return { provider: rawProvider, model, dimensions };\n}\n\nclass BedrockEmbedder implements EmbeddingProvider {\n  readonly kind = 'bedrock' as const;\n  readonly model: string;\n  readonly dimensions: number;\n  private client: BedrockRuntimeClient;\n\n  constructor(params: { model: string; dimensions: number }) {\n    this.model = params.model;\n    this.dimensions = params.dimensions;\n    const region =\n      process.env.BEDROCK_AWS_DEFAULT_REGION ??\n      process.env.AWS_REGION ??\n      'us-east-1';\n    // Default credential provider chain — identical to host's existing\n    // Bedrock client. In ECS this resolves to the task role.\n    this.client = new BedrockRuntimeClient({ region });\n  }\n\n  async embed(text: string): Promise<number[]> {\n    const input = text.trim();\n    if (!input) {\n      throw new Error('Cannot embed empty text');\n    }\n    const body = {\n      inputText: input,\n      dimensions: this.dimensions,\n      normalize: true,\n    };\n    const command = new InvokeModelCommand({\n      modelId: this.model,\n      contentType: 'application/json',\n      accept: 'application/json',\n      body: new TextEncoder().encode(JSON.stringify(body)),\n    });\n    const response = await this.client.send(command);\n    const decoded = JSON.parse(new TextDecoder().decode(response.body)) as {\n      embedding: number[];\n    };\n    if (!Array.isArray(decoded.embedding)) {\n      throw new Error('Bedrock embedding response missing `embedding` field');\n    }\n    return decoded.embedding;\n  }\n\n  async embedBatch(texts: string[]): Promise<number[][]> {\n    // Titan embed v2 has no native batch API — sequential calls are the\n    // documented pattern. Parallelism here can trip per-account TPS ceilings,\n    // so we keep it serial. For Phase 4 we can introduce a bounded queue.\n    const out: number[][] = [];\n    for (const text of texts) {\n      out.push(await this.embed(text));\n    }\n    return out;\n  }\n}\n\nclass OpenAIEmbedder implements EmbeddingProvider {\n  readonly kind = 'openai' as const;\n  readonly model: string;\n  readonly dimensions: number;\n  private apiKey: string;\n  private baseUrl: string;\n\n  constructor(params: { model: string; dimensions: number }) {\n    this.model = params.model;\n    this.dimensions = params.dimensions;\n    const apiKey = process.env.OPENAI_API_KEY;\n    if (!apiKey) {\n      throw new Error(\n        'OPENAI_API_KEY is not set — required when MEMORY_EMBEDDINGS_PROVIDER=openai'\n      );\n    }\n    this.apiKey = apiKey;\n    this.baseUrl = process.env.OPENAI_BASEURL ?? 'https://api.openai.com/v1';\n  }\n\n  private async call(texts: string[]): Promise<number[][]> {\n    const res = await fetch(`${this.baseUrl}/embeddings`, {\n      method: 'POST',\n      headers: {\n        'Content-Type': 'application/json',\n        Authorization: `Bearer ${this.apiKey}`,\n      },\n      body: JSON.stringify({ model: this.model, input: texts }),\n    });\n    if (!res.ok) {\n      const errText = await res.text();\n      throw new Error(`OpenAI embeddings ${res.status}: ${errText}`);\n    }\n    const json = (await res.json()) as { data: Array<{ embedding: number[] }> };\n    return json.data.map((d) => d.embedding);\n  }\n\n  async embed(text: string): Promise<number[]> {\n    const [vec] = await this.call([text]);\n    return vec;\n  }\n\n  async embedBatch(texts: string[]): Promise<number[][]> {\n    if (texts.length === 0) return [];\n    return this.call(texts);\n  }\n}\n\nlet sharedEmbedder: EmbeddingProvider | null = null;\n\n/**\n * Lazy singleton accessor. Same idiom as host's\n * `getSharedSummaryBedrockClient()`.\n */\nexport function getMemoryEmbedder(): EmbeddingProvider {\n  if (sharedEmbedder) return sharedEmbedder;\n  const cfg = resolveConfig();\n  switch (cfg.provider) {\n    case 'bedrock':\n      sharedEmbedder = new BedrockEmbedder({\n        model: cfg.model,\n        dimensions: cfg.dimensions,\n      });\n      break;\n    case 'openai':\n      sharedEmbedder = new OpenAIEmbedder({\n        model: cfg.model,\n        dimensions: cfg.dimensions,\n      });\n      break;\n  }\n  return sharedEmbedder;\n}\n\n/** Test hook — drops the cached embedder so the next call re-reads env. */\nexport function resetMemoryEmbedder(): void {\n  sharedEmbedder = null;\n}\n"],"names":["DEFAULT_MEMORY_PROVIDER","DEFAULT_MEMORY_MODEL","DEFAULT_MEMORY_DIMENSIONS","BedrockRuntimeClient","InvokeModelCommand"],"mappings":"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"}