cencori 1.3.0 → 1.3.1

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/README.md CHANGED
@@ -130,6 +130,46 @@ const response = await cencori.ai.embeddings({
130
130
  console.log(response.embeddings[0]); // [0.1, 0.2, ...]
131
131
  ```
132
132
 
133
+ ### Vision (Image Understanding)
134
+
135
+ Analyze, describe, OCR, and classify images. Routes across OpenAI, Anthropic, and Google vision-capable models.
136
+
137
+ ```typescript
138
+ // General analysis with a custom prompt
139
+ const result = await cencori.vision.analyze({
140
+ image: { url: 'https://example.com/photo.jpg' },
141
+ prompt: 'What breed of dog is this?'
142
+ });
143
+
144
+ // OCR from a local file
145
+ import fs from 'node:fs';
146
+ const buf = fs.readFileSync('receipt.png');
147
+ const { text } = await cencori.vision.ocr({
148
+ image: { base64: buf.toString('base64'), mimeType: 'image/png' }
149
+ });
150
+
151
+ // Structured classification
152
+ const { classification } = await cencori.vision.classify({
153
+ image: { url: 'https://example.com/product.jpg' }
154
+ });
155
+ // classification: { primary_category, tags, objects, safe_for_work, ... }
156
+ ```
157
+
158
+ Drop-in React uploader for your own product (`cencori/react`):
159
+
160
+ ```tsx
161
+ import { VisionUploader } from 'cencori/react';
162
+
163
+ <VisionUploader
164
+ endpoint="https://cencori.com/api/ai/vision"
165
+ apiKey={process.env.NEXT_PUBLIC_CENCORI_KEY}
166
+ task="describe"
167
+ onResult={(r) => console.log(r)}
168
+ />
169
+ ```
170
+
171
+ Full API in [docs](https://cencori.com/docs/ai/endpoints/vision).
172
+
133
173
  ### Image Generation
134
174
 
135
175
  Generate images from text prompts using multiple providers:
package/dist/index.d.mts CHANGED
@@ -3,7 +3,7 @@ export { j as ChatMessage, a as ChatRequest, b as ChatResponse, o as CodeInterpr
3
3
  import { AINamespace } from './ai/index.mjs';
4
4
  export { StreamChunk } from './ai/index.mjs';
5
5
  import { VisionNamespace } from './vision/index.mjs';
6
- export { VisionClassification, VisionClassifyResult, VisionCost, VisionDescribeResult, VisionImage, VisionOcrResult, VisionProvider, VisionRequest, VisionResult, VisionTask, VisionUsage } from './vision/index.mjs';
6
+ export { VisionClassification, VisionClassifyResult, VisionCost, VisionDescribeResult, VisionImage, VisionOcrResult, VisionProvider, VisionRequest, VisionResult, VisionStreamChunk, VisionTask, VisionUsage } from './vision/index.mjs';
7
7
  import { ComputeNamespace } from './compute/index.mjs';
8
8
  import { WorkflowNamespace } from './workflow/index.mjs';
9
9
  import { StorageNamespace } from './storage/index.mjs';
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { j as ChatMessage, a as ChatRequest, b as ChatResponse, o as CodeInterpr
3
3
  import { AINamespace } from './ai/index.js';
4
4
  export { StreamChunk } from './ai/index.js';
5
5
  import { VisionNamespace } from './vision/index.js';
6
- export { VisionClassification, VisionClassifyResult, VisionCost, VisionDescribeResult, VisionImage, VisionOcrResult, VisionProvider, VisionRequest, VisionResult, VisionTask, VisionUsage } from './vision/index.js';
6
+ export { VisionClassification, VisionClassifyResult, VisionCost, VisionDescribeResult, VisionImage, VisionOcrResult, VisionProvider, VisionRequest, VisionResult, VisionStreamChunk, VisionTask, VisionUsage } from './vision/index.js';
7
7
  import { ComputeNamespace } from './compute/index.js';
8
8
  import { WorkflowNamespace } from './workflow/index.js';
9
9
  import { StorageNamespace } from './storage/index.js';
package/dist/index.js CHANGED
@@ -676,6 +676,57 @@ var VisionNamespace = class {
676
676
  async classify(request) {
677
677
  return this.post("/api/ai/vision/classify", request);
678
678
  }
679
+ /**
680
+ * Stream a vision analysis. Yields `{ delta }` chunks as text arrives, then
681
+ * a final `{ done: true, model, provider, usage, cost }` chunk with totals.
682
+ *
683
+ * @example
684
+ * for await (const chunk of cencori.vision.analyzeStream({ image: { url } })) {
685
+ * if (chunk.delta) process.stdout.write(chunk.delta);
686
+ * if (chunk.done) console.log(chunk.cost);
687
+ * }
688
+ */
689
+ async *analyzeStream(request) {
690
+ const body = { ...toBody(request), stream: true };
691
+ const response = await fetch(`${this.config.baseUrl}/api/ai/vision`, {
692
+ method: "POST",
693
+ headers: {
694
+ "CENCORI_API_KEY": this.config.apiKey,
695
+ "Content-Type": "application/json",
696
+ ...this.config.headers
697
+ },
698
+ body: JSON.stringify(body)
699
+ });
700
+ if (!response.ok) {
701
+ const data = await response.json().catch(() => ({}));
702
+ const message = data && typeof data === "object" && "message" in data && typeof data.message === "string" ? data.message : `Vision stream request failed with status ${response.status}`;
703
+ throw new Error(message);
704
+ }
705
+ if (!response.body) throw new Error("Response body is null");
706
+ const reader = response.body.getReader();
707
+ const decoder = new TextDecoder();
708
+ let buffer = "";
709
+ try {
710
+ while (true) {
711
+ const { done, value } = await reader.read();
712
+ if (done) break;
713
+ buffer += decoder.decode(value, { stream: true });
714
+ const lines = buffer.split("\n");
715
+ buffer = lines.pop() ?? "";
716
+ for (const line of lines) {
717
+ if (!line.startsWith("data: ")) continue;
718
+ const data = line.slice(6);
719
+ if (data === "[DONE]") return;
720
+ try {
721
+ yield JSON.parse(data);
722
+ } catch {
723
+ }
724
+ }
725
+ }
726
+ } finally {
727
+ reader.releaseLock();
728
+ }
729
+ }
679
730
  async post(path, request) {
680
731
  const response = await fetch(`${this.config.baseUrl}${path}`, {
681
732
  method: "POST",