// src/photo/vision.ts
import { zodToJsonSchema } from "zod-to-json-schema";
function isToolUseBlock(block) {
  return block.type === "tool_use";
}
function extractConfidence(data) {
  if (data && typeof data === "object" && "confidence" in data) {
    const c = data.confidence;
    if (typeof c === "number" && c >= 0 && c <= 1) return c;
  }
  return 1;
}
async function extractWithVision(args) {
  const maxRetries = args.maxRetries ?? 2;
  const maxTokens = args.maxTokens ?? 2048;
  const toolName = args.toolName ?? "extract";
  const inputSchema = zodToJsonSchema(args.schema, {
    target: "openApi3",
    $refStrategy: "none"
  });
  const base64 = args.imageBytes.toString("base64");
  const baseMessages = [
    {
      role: "user",
      content: [
        {
          type: "image",
          source: {
            type: "base64",
            media_type: args.mimeType,
            data: base64
          }
        },
        { type: "text", text: args.userPrompt }
      ]
    }
  ];
  let lastReason = "no response from model";
  let lastRawText;
  const totalAttempts = Math.max(1, maxRetries + 1);
  for (let attempt = 0; attempt < totalAttempts; attempt++) {
    const messages = attempt === 0 ? baseMessages : [
      ...baseMessages,
      {
        role: "user",
        content: [
          {
            type: "text",
            text: `Your previous attempt failed validation: ${lastReason}. Call the '${toolName}' tool again with input that strictly matches the schema.`
          }
        ]
      }
    ];
    const response = await args.client.messages.create({
      model: args.model,
      max_tokens: maxTokens,
      system: args.systemPrompt,
      messages,
      tools: [
        {
          name: toolName,
          description: "Return the structured extraction for the supplied image. Always call this tool \u2014 do not respond in plain text.",
          input_schema: inputSchema
        }
      ],
      tool_choice: { type: "tool", name: toolName }
    });
    const toolUse = response.content.find(isToolUseBlock);
    if (!toolUse) {
      const textBlock = response.content.find(
        (b) => b.type === "text"
      );
      lastRawText = textBlock?.text;
      lastReason = "model did not emit a tool_use block";
      continue;
    }
    const parsed = args.schema.safeParse(toolUse.input);
    if (parsed.success) {
      return {
        data: parsed.data,
        confidence: extractConfidence(parsed.data),
        rawText: lastRawText
      };
    }
    lastReason = parsed.error.issues.slice(0, 3).map((i) => `${i.path.join(".") || "(root)"}: ${i.message}`).join("; ");
    lastRawText = typeof toolUse.input === "string" ? toolUse.input : JSON.stringify(toolUse.input);
  }
  return {
    data: null,
    confidence: 0,
    rawText: lastRawText,
    reason: lastReason
  };
}

// src/photo/orchestrator.ts
var DEFAULT_MIME = "image/jpeg";
async function processPhoto(args) {
  const bytes = await args.source.fetchBytes();
  const memoisedSource = {
    ...args.source,
    fetchBytes: async () => bytes
  };
  const mimeType = args.source.mimeType ?? DEFAULT_MIME;
  const [persistResult, visionResult] = await Promise.all([
    args.storage.persist(memoisedSource, {
      agentId: args.ctx.agentId,
      ...args.ctx.userId !== void 0 ? { userId: args.ctx.userId } : {}
    }),
    extractWithVision({
      client: args.vision.client,
      model: args.vision.model,
      imageBytes: bytes,
      mimeType,
      schema: args.schema,
      systemPrompt: args.systemPrompt,
      userPrompt: args.userPrompt,
      ...args.vision.maxRetries !== void 0 ? { maxRetries: args.vision.maxRetries } : {},
      ...args.vision.maxTokens !== void 0 ? { maxTokens: args.vision.maxTokens } : {}
    })
  ]);
  return {
    data: visionResult.data,
    confidence: visionResult.confidence,
    ...visionResult.data === null ? {
      partial: {
        reason: visionResult.reason ?? "unknown extraction failure",
        ...visionResult.rawText !== void 0 ? { rawText: visionResult.rawText } : {}
      }
    } : {},
    photoUrl: persistResult.url,
    storageKey: persistResult.key
  };
}

// src/photo/storage-memory.ts
import { randomUUID } from "crypto";
var MemoryPhotoStorage = class {
  store = /* @__PURE__ */ new Map();
  async persist(source, opts) {
    const bytes = await source.fetchBytes();
    const id = randomUUID();
    const key = opts.userId ? `${opts.agentId}/${opts.userId}/${id}` : `${opts.agentId}/${id}`;
    this.store.set(key, bytes);
    return { url: `memory://${key}`, key };
  }
  get(key) {
    return this.store.get(key);
  }
  has(key) {
    return this.store.has(key);
  }
  size() {
    return this.store.size;
  }
  clear() {
    this.store.clear();
  }
};
export {
  MemoryPhotoStorage,
  extractWithVision,
  processPhoto
};
//# sourceMappingURL=index.mjs.map