feat(deploy): Gemini som committat prod-läge + hermetiska eval-fixtures
- config.ts: tillåt AAMOS_MODE=http|gemini i prod; mock förbjudet - first-deploy.sh: Grind 1 accepterar gemini (kräv GEMINI_API_KEY); Grind 5 kör eval:scan mot gemini; Grind 8 behandlar gemini som riktig AI - gemini.ts: stöd för data-URLer (inline base64-bilder) i fetchOneImage - eval-fixtures: 4 lokala bilder (fridge-1, fridge-2, label-1, receipt-1) - scan-eval.ts: läser lokala fixtures, inga externa hämtningar - capture-eval.ts: använder lokal fixture som data-URL
This commit is contained in:
@@ -291,6 +291,20 @@ export class GeminiAamosClient implements AamosClient {
|
||||
}
|
||||
}
|
||||
|
||||
private async fetchOneImage(url: string, timeoutMs: number): Promise<{ inline_data: { mime_type: string; data: string } }> {
|
||||
if (url.startsWith("data:")) {
|
||||
const match = url.match(/^data:([^;]+);base64,(.+)$/);
|
||||
if (match && match[1] && match[2]) {
|
||||
return { inline_data: { mime_type: match[1], data: match[2] } };
|
||||
}
|
||||
throw new Error(`Ogiltig data-URL: ${url.slice(0, 50)}`);
|
||||
}
|
||||
const res = await this.cfg.fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
|
||||
if (!res.ok) throw new Error(`Kunde inte hämta bild: ${res.status} ${url}`);
|
||||
const buf = Buffer.from(await res.arrayBuffer());
|
||||
return { inline_data: { mime_type: this.mimeType(buf), data: buf.toString("base64") } };
|
||||
}
|
||||
|
||||
private async analyzeStorageImage(
|
||||
taskType: "ANALYZE_FRIDGE_IMAGE" | "ANALYZE_PANTRY_IMAGE",
|
||||
input: TaskInput<"ANALYZE_FRIDGE_IMAGE">,
|
||||
@@ -300,12 +314,7 @@ export class GeminiAamosClient implements AamosClient {
|
||||
const started = Date.now();
|
||||
|
||||
const imageParts = await Promise.all(
|
||||
input.imageUrls.slice(0, 6).map(async (url) => {
|
||||
const res = await this.cfg.fetchImpl(url, { signal: AbortSignal.timeout(this.cfg.timeoutMs) });
|
||||
if (!res.ok) throw new Error(`Kunde inte hämta bild: ${res.status} ${url}`);
|
||||
const buf = Buffer.from(await res.arrayBuffer());
|
||||
return { inline_data: { mime_type: this.mimeType(buf), data: buf.toString("base64") } };
|
||||
}),
|
||||
input.imageUrls.slice(0, 6).map((url) => this.fetchOneImage(url, this.cfg.timeoutMs)),
|
||||
);
|
||||
|
||||
const estimatedCostUsd = imageParts.length * COST_ESTIMATE_PER_IMAGE_USD;
|
||||
@@ -552,12 +561,7 @@ export class GeminiAamosClient implements AamosClient {
|
||||
timeoutMs: number,
|
||||
): Promise<Array<{ inline_data: { mime_type: string; data: string } }>> {
|
||||
return Promise.all(
|
||||
imageUrls.slice(0, 6).map(async (url) => {
|
||||
const res = await this.cfg.fetchImpl(url, { signal: AbortSignal.timeout(timeoutMs) });
|
||||
if (!res.ok) throw new Error(`Kunde inte hämta bild: ${res.status} ${url}`);
|
||||
const buf = Buffer.from(await res.arrayBuffer());
|
||||
return { inline_data: { mime_type: this.mimeType(buf), data: buf.toString("base64") } };
|
||||
}),
|
||||
imageUrls.slice(0, 6).map((url) => this.fetchOneImage(url, timeoutMs)),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user