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 +40 -0
- package/dist/index.d.mts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +51 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +51 -0
- package/dist/index.mjs.map +1 -1
- package/dist/vision/index.d.mts +21 -1
- package/dist/vision/index.d.ts +21 -1
- package/dist/vision/index.js +51 -0
- package/dist/vision/index.js.map +1 -1
- package/dist/vision/index.mjs +51 -0
- package/dist/vision/index.mjs.map +1 -1
- package/package.json +1 -1
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",
|