cencori 1.2.1 → 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.
@@ -0,0 +1,144 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/vision/index.ts
21
+ var vision_exports = {};
22
+ __export(vision_exports, {
23
+ VisionNamespace: () => VisionNamespace
24
+ });
25
+ module.exports = __toCommonJS(vision_exports);
26
+ function toBody(request) {
27
+ const body = {
28
+ prompt: request.prompt,
29
+ model: request.model,
30
+ max_tokens: request.maxTokens,
31
+ temperature: request.temperature,
32
+ response_format: request.responseFormat
33
+ };
34
+ if (request.image.url) {
35
+ body.image_url = request.image.url;
36
+ } else if (request.image.base64) {
37
+ body.image_base64 = request.image.base64;
38
+ body.mime_type = request.image.mimeType;
39
+ } else {
40
+ throw new Error("vision request requires image.url or image.base64");
41
+ }
42
+ return body;
43
+ }
44
+ var VisionNamespace = class {
45
+ constructor(config) {
46
+ this.config = config;
47
+ }
48
+ /**
49
+ * General image analysis — provide your own prompt.
50
+ * Default prompt: "Describe this image in detail."
51
+ */
52
+ async analyze(request) {
53
+ return this.post("/api/ai/vision", request);
54
+ }
55
+ /** Describe the image in rich detail. */
56
+ async describe(request) {
57
+ return this.post("/api/ai/vision/describe", request);
58
+ }
59
+ /** Extract all visible text from the image. */
60
+ async ocr(request) {
61
+ return this.post("/api/ai/vision/ocr", request);
62
+ }
63
+ /** Return structured tags, objects, and category classification. */
64
+ async classify(request) {
65
+ return this.post("/api/ai/vision/classify", request);
66
+ }
67
+ /**
68
+ * Stream a vision analysis. Yields `{ delta }` chunks as text arrives, then
69
+ * a final `{ done: true, model, provider, usage, cost }` chunk with totals.
70
+ *
71
+ * @example
72
+ * for await (const chunk of cencori.vision.analyzeStream({ image: { url } })) {
73
+ * if (chunk.delta) process.stdout.write(chunk.delta);
74
+ * if (chunk.done) console.log(chunk.cost);
75
+ * }
76
+ */
77
+ async *analyzeStream(request) {
78
+ const body = { ...toBody(request), stream: true };
79
+ const response = await fetch(`${this.config.baseUrl}/api/ai/vision`, {
80
+ method: "POST",
81
+ headers: {
82
+ "CENCORI_API_KEY": this.config.apiKey,
83
+ "Content-Type": "application/json",
84
+ ...this.config.headers
85
+ },
86
+ body: JSON.stringify(body)
87
+ });
88
+ if (!response.ok) {
89
+ const data = await response.json().catch(() => ({}));
90
+ const message = data && typeof data === "object" && "message" in data && typeof data.message === "string" ? data.message : `Vision stream request failed with status ${response.status}`;
91
+ throw new Error(message);
92
+ }
93
+ if (!response.body) throw new Error("Response body is null");
94
+ const reader = response.body.getReader();
95
+ const decoder = new TextDecoder();
96
+ let buffer = "";
97
+ try {
98
+ while (true) {
99
+ const { done, value } = await reader.read();
100
+ if (done) break;
101
+ buffer += decoder.decode(value, { stream: true });
102
+ const lines = buffer.split("\n");
103
+ buffer = lines.pop() ?? "";
104
+ for (const line of lines) {
105
+ if (!line.startsWith("data: ")) continue;
106
+ const data = line.slice(6);
107
+ if (data === "[DONE]") return;
108
+ try {
109
+ yield JSON.parse(data);
110
+ } catch {
111
+ }
112
+ }
113
+ }
114
+ } finally {
115
+ reader.releaseLock();
116
+ }
117
+ }
118
+ async post(path, request) {
119
+ const response = await fetch(`${this.config.baseUrl}${path}`, {
120
+ method: "POST",
121
+ headers: {
122
+ "CENCORI_API_KEY": this.config.apiKey,
123
+ "Content-Type": "application/json",
124
+ ...this.config.headers
125
+ },
126
+ body: JSON.stringify(toBody(request))
127
+ });
128
+ const data = await response.json();
129
+ if (!response.ok) {
130
+ const message = data && typeof data === "object" && "message" in data && typeof data.message === "string" ? data.message : `Vision request failed with status ${response.status}`;
131
+ const code = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : "request_failed";
132
+ const err = new Error(message);
133
+ err.code = code;
134
+ err.details = data;
135
+ throw err;
136
+ }
137
+ return data;
138
+ }
139
+ };
140
+ // Annotate the CommonJS export names for ESM import in node:
141
+ 0 && (module.exports = {
142
+ VisionNamespace
143
+ });
144
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vision/index.ts"],"sourcesContent":["/**\n * Vision API — analyze, describe, OCR, and classify images.\n *\n * @example\n * const result = await cencori.vision.analyze({\n * image: { url: 'https://example.com/photo.jpg' },\n * prompt: 'What breed of dog is this?',\n * });\n *\n * @example\n * const { text } = await cencori.vision.ocr({ image: { base64, mimeType: 'image/png' } });\n */\n\nimport type { CencoriConfig } from '../types';\n\nexport type VisionProvider = 'openai' | 'anthropic' | 'google';\n\nexport type VisionTask = 'analyze' | 'describe' | 'ocr' | 'classify';\n\nexport interface VisionImage {\n /** https:// URL or data: URL. Exactly one of `url` or `base64` must be set. */\n url?: string;\n /** Raw base64 (no data: prefix). Requires `mimeType`. */\n base64?: string;\n /** Image mime type. Required when using `base64`. */\n mimeType?: string;\n}\n\nexport interface VisionRequest {\n image: VisionImage;\n prompt?: string;\n model?: string;\n maxTokens?: number;\n temperature?: number;\n responseFormat?: 'text' | 'json';\n}\n\nexport interface VisionStreamChunk {\n delta?: string;\n done?: boolean;\n model?: string;\n provider?: VisionProvider;\n usage?: VisionUsage;\n cost?: VisionCost;\n error?: string;\n}\n\nexport interface VisionUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\nexport interface VisionCost {\n providerCostUsd: number;\n cencoriChargeUsd: number;\n markupPercentage: number;\n}\n\nexport interface VisionResult {\n analysis: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionDescribeResult {\n description: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionOcrResult {\n text: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionClassification {\n primary_category?: string;\n tags?: string[];\n objects?: string[];\n safe_for_work?: boolean;\n confidence?: number;\n summary?: string;\n [key: string]: unknown;\n}\n\nexport interface VisionClassifyResult {\n classification: VisionClassification | string;\n raw: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\ninterface JsonBody {\n image_url?: string;\n image_base64?: string;\n mime_type?: string;\n prompt?: string;\n model?: string;\n max_tokens?: number;\n temperature?: number;\n response_format?: 'text' | 'json';\n}\n\nfunction toBody(request: VisionRequest): JsonBody {\n const body: JsonBody = {\n prompt: request.prompt,\n model: request.model,\n max_tokens: request.maxTokens,\n temperature: request.temperature,\n response_format: request.responseFormat,\n };\n if (request.image.url) {\n body.image_url = request.image.url;\n } else if (request.image.base64) {\n body.image_base64 = request.image.base64;\n body.mime_type = request.image.mimeType;\n } else {\n throw new Error('vision request requires image.url or image.base64');\n }\n return body;\n}\n\nexport class VisionNamespace {\n private config: Required<CencoriConfig>;\n\n constructor(config: Required<CencoriConfig>) {\n this.config = config;\n }\n\n /**\n * General image analysis — provide your own prompt.\n * Default prompt: \"Describe this image in detail.\"\n */\n async analyze(request: VisionRequest): Promise<VisionResult> {\n return this.post<VisionResult>('/api/ai/vision', request);\n }\n\n /** Describe the image in rich detail. */\n async describe(request: VisionRequest): Promise<VisionDescribeResult> {\n return this.post<VisionDescribeResult>('/api/ai/vision/describe', request);\n }\n\n /** Extract all visible text from the image. */\n async ocr(request: VisionRequest): Promise<VisionOcrResult> {\n return this.post<VisionOcrResult>('/api/ai/vision/ocr', request);\n }\n\n /** Return structured tags, objects, and category classification. */\n async classify(request: VisionRequest): Promise<VisionClassifyResult> {\n return this.post<VisionClassifyResult>('/api/ai/vision/classify', request);\n }\n\n /**\n * Stream a vision analysis. Yields `{ delta }` chunks as text arrives, then\n * a final `{ done: true, model, provider, usage, cost }` chunk with totals.\n *\n * @example\n * for await (const chunk of cencori.vision.analyzeStream({ image: { url } })) {\n * if (chunk.delta) process.stdout.write(chunk.delta);\n * if (chunk.done) console.log(chunk.cost);\n * }\n */\n async *analyzeStream(request: VisionRequest): AsyncGenerator<VisionStreamChunk, void, unknown> {\n const body = { ...toBody(request), stream: true };\n const response = await fetch(`${this.config.baseUrl}/api/ai/vision`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n const data = await response.json().catch(() => ({}));\n const message =\n (data && typeof data === 'object' && 'message' in data && typeof data.message === 'string')\n ? data.message\n : `Vision stream request failed with status ${response.status}`;\n throw new Error(message);\n }\n if (!response.body) throw new Error('Response body is null');\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6);\n if (data === '[DONE]') return;\n try {\n yield JSON.parse(data) as VisionStreamChunk;\n } catch {\n // skip malformed frame\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n private async post<T>(path: string, request: VisionRequest): Promise<T> {\n const response = await fetch(`${this.config.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(toBody(request)),\n });\n const data = await response.json();\n if (!response.ok) {\n const message =\n (data && typeof data === 'object' && 'message' in data && typeof data.message === 'string')\n ? data.message\n : `Vision request failed with status ${response.status}`;\n const code =\n (data && typeof data === 'object' && 'error' in data && typeof data.error === 'string')\n ? data.error\n : 'request_failed';\n const err = new Error(message) as Error & { code?: string; details?: unknown };\n err.code = code;\n err.details = data;\n throw err;\n }\n return data as T;\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,119 @@
1
+ // src/vision/index.ts
2
+ function toBody(request) {
3
+ const body = {
4
+ prompt: request.prompt,
5
+ model: request.model,
6
+ max_tokens: request.maxTokens,
7
+ temperature: request.temperature,
8
+ response_format: request.responseFormat
9
+ };
10
+ if (request.image.url) {
11
+ body.image_url = request.image.url;
12
+ } else if (request.image.base64) {
13
+ body.image_base64 = request.image.base64;
14
+ body.mime_type = request.image.mimeType;
15
+ } else {
16
+ throw new Error("vision request requires image.url or image.base64");
17
+ }
18
+ return body;
19
+ }
20
+ var VisionNamespace = class {
21
+ constructor(config) {
22
+ this.config = config;
23
+ }
24
+ /**
25
+ * General image analysis — provide your own prompt.
26
+ * Default prompt: "Describe this image in detail."
27
+ */
28
+ async analyze(request) {
29
+ return this.post("/api/ai/vision", request);
30
+ }
31
+ /** Describe the image in rich detail. */
32
+ async describe(request) {
33
+ return this.post("/api/ai/vision/describe", request);
34
+ }
35
+ /** Extract all visible text from the image. */
36
+ async ocr(request) {
37
+ return this.post("/api/ai/vision/ocr", request);
38
+ }
39
+ /** Return structured tags, objects, and category classification. */
40
+ async classify(request) {
41
+ return this.post("/api/ai/vision/classify", request);
42
+ }
43
+ /**
44
+ * Stream a vision analysis. Yields `{ delta }` chunks as text arrives, then
45
+ * a final `{ done: true, model, provider, usage, cost }` chunk with totals.
46
+ *
47
+ * @example
48
+ * for await (const chunk of cencori.vision.analyzeStream({ image: { url } })) {
49
+ * if (chunk.delta) process.stdout.write(chunk.delta);
50
+ * if (chunk.done) console.log(chunk.cost);
51
+ * }
52
+ */
53
+ async *analyzeStream(request) {
54
+ const body = { ...toBody(request), stream: true };
55
+ const response = await fetch(`${this.config.baseUrl}/api/ai/vision`, {
56
+ method: "POST",
57
+ headers: {
58
+ "CENCORI_API_KEY": this.config.apiKey,
59
+ "Content-Type": "application/json",
60
+ ...this.config.headers
61
+ },
62
+ body: JSON.stringify(body)
63
+ });
64
+ if (!response.ok) {
65
+ const data = await response.json().catch(() => ({}));
66
+ const message = data && typeof data === "object" && "message" in data && typeof data.message === "string" ? data.message : `Vision stream request failed with status ${response.status}`;
67
+ throw new Error(message);
68
+ }
69
+ if (!response.body) throw new Error("Response body is null");
70
+ const reader = response.body.getReader();
71
+ const decoder = new TextDecoder();
72
+ let buffer = "";
73
+ try {
74
+ while (true) {
75
+ const { done, value } = await reader.read();
76
+ if (done) break;
77
+ buffer += decoder.decode(value, { stream: true });
78
+ const lines = buffer.split("\n");
79
+ buffer = lines.pop() ?? "";
80
+ for (const line of lines) {
81
+ if (!line.startsWith("data: ")) continue;
82
+ const data = line.slice(6);
83
+ if (data === "[DONE]") return;
84
+ try {
85
+ yield JSON.parse(data);
86
+ } catch {
87
+ }
88
+ }
89
+ }
90
+ } finally {
91
+ reader.releaseLock();
92
+ }
93
+ }
94
+ async post(path, request) {
95
+ const response = await fetch(`${this.config.baseUrl}${path}`, {
96
+ method: "POST",
97
+ headers: {
98
+ "CENCORI_API_KEY": this.config.apiKey,
99
+ "Content-Type": "application/json",
100
+ ...this.config.headers
101
+ },
102
+ body: JSON.stringify(toBody(request))
103
+ });
104
+ const data = await response.json();
105
+ if (!response.ok) {
106
+ const message = data && typeof data === "object" && "message" in data && typeof data.message === "string" ? data.message : `Vision request failed with status ${response.status}`;
107
+ const code = data && typeof data === "object" && "error" in data && typeof data.error === "string" ? data.error : "request_failed";
108
+ const err = new Error(message);
109
+ err.code = code;
110
+ err.details = data;
111
+ throw err;
112
+ }
113
+ return data;
114
+ }
115
+ };
116
+ export {
117
+ VisionNamespace
118
+ };
119
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/vision/index.ts"],"sourcesContent":["/**\n * Vision API — analyze, describe, OCR, and classify images.\n *\n * @example\n * const result = await cencori.vision.analyze({\n * image: { url: 'https://example.com/photo.jpg' },\n * prompt: 'What breed of dog is this?',\n * });\n *\n * @example\n * const { text } = await cencori.vision.ocr({ image: { base64, mimeType: 'image/png' } });\n */\n\nimport type { CencoriConfig } from '../types';\n\nexport type VisionProvider = 'openai' | 'anthropic' | 'google';\n\nexport type VisionTask = 'analyze' | 'describe' | 'ocr' | 'classify';\n\nexport interface VisionImage {\n /** https:// URL or data: URL. Exactly one of `url` or `base64` must be set. */\n url?: string;\n /** Raw base64 (no data: prefix). Requires `mimeType`. */\n base64?: string;\n /** Image mime type. Required when using `base64`. */\n mimeType?: string;\n}\n\nexport interface VisionRequest {\n image: VisionImage;\n prompt?: string;\n model?: string;\n maxTokens?: number;\n temperature?: number;\n responseFormat?: 'text' | 'json';\n}\n\nexport interface VisionStreamChunk {\n delta?: string;\n done?: boolean;\n model?: string;\n provider?: VisionProvider;\n usage?: VisionUsage;\n cost?: VisionCost;\n error?: string;\n}\n\nexport interface VisionUsage {\n promptTokens: number;\n completionTokens: number;\n totalTokens: number;\n}\n\nexport interface VisionCost {\n providerCostUsd: number;\n cencoriChargeUsd: number;\n markupPercentage: number;\n}\n\nexport interface VisionResult {\n analysis: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionDescribeResult {\n description: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionOcrResult {\n text: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\nexport interface VisionClassification {\n primary_category?: string;\n tags?: string[];\n objects?: string[];\n safe_for_work?: boolean;\n confidence?: number;\n summary?: string;\n [key: string]: unknown;\n}\n\nexport interface VisionClassifyResult {\n classification: VisionClassification | string;\n raw: string;\n model: string;\n provider: VisionProvider;\n usage: VisionUsage;\n cost: VisionCost;\n}\n\ninterface JsonBody {\n image_url?: string;\n image_base64?: string;\n mime_type?: string;\n prompt?: string;\n model?: string;\n max_tokens?: number;\n temperature?: number;\n response_format?: 'text' | 'json';\n}\n\nfunction toBody(request: VisionRequest): JsonBody {\n const body: JsonBody = {\n prompt: request.prompt,\n model: request.model,\n max_tokens: request.maxTokens,\n temperature: request.temperature,\n response_format: request.responseFormat,\n };\n if (request.image.url) {\n body.image_url = request.image.url;\n } else if (request.image.base64) {\n body.image_base64 = request.image.base64;\n body.mime_type = request.image.mimeType;\n } else {\n throw new Error('vision request requires image.url or image.base64');\n }\n return body;\n}\n\nexport class VisionNamespace {\n private config: Required<CencoriConfig>;\n\n constructor(config: Required<CencoriConfig>) {\n this.config = config;\n }\n\n /**\n * General image analysis — provide your own prompt.\n * Default prompt: \"Describe this image in detail.\"\n */\n async analyze(request: VisionRequest): Promise<VisionResult> {\n return this.post<VisionResult>('/api/ai/vision', request);\n }\n\n /** Describe the image in rich detail. */\n async describe(request: VisionRequest): Promise<VisionDescribeResult> {\n return this.post<VisionDescribeResult>('/api/ai/vision/describe', request);\n }\n\n /** Extract all visible text from the image. */\n async ocr(request: VisionRequest): Promise<VisionOcrResult> {\n return this.post<VisionOcrResult>('/api/ai/vision/ocr', request);\n }\n\n /** Return structured tags, objects, and category classification. */\n async classify(request: VisionRequest): Promise<VisionClassifyResult> {\n return this.post<VisionClassifyResult>('/api/ai/vision/classify', request);\n }\n\n /**\n * Stream a vision analysis. Yields `{ delta }` chunks as text arrives, then\n * a final `{ done: true, model, provider, usage, cost }` chunk with totals.\n *\n * @example\n * for await (const chunk of cencori.vision.analyzeStream({ image: { url } })) {\n * if (chunk.delta) process.stdout.write(chunk.delta);\n * if (chunk.done) console.log(chunk.cost);\n * }\n */\n async *analyzeStream(request: VisionRequest): AsyncGenerator<VisionStreamChunk, void, unknown> {\n const body = { ...toBody(request), stream: true };\n const response = await fetch(`${this.config.baseUrl}/api/ai/vision`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(body),\n });\n if (!response.ok) {\n const data = await response.json().catch(() => ({}));\n const message =\n (data && typeof data === 'object' && 'message' in data && typeof data.message === 'string')\n ? data.message\n : `Vision stream request failed with status ${response.status}`;\n throw new Error(message);\n }\n if (!response.body) throw new Error('Response body is null');\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n for (const line of lines) {\n if (!line.startsWith('data: ')) continue;\n const data = line.slice(6);\n if (data === '[DONE]') return;\n try {\n yield JSON.parse(data) as VisionStreamChunk;\n } catch {\n // skip malformed frame\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n\n private async post<T>(path: string, request: VisionRequest): Promise<T> {\n const response = await fetch(`${this.config.baseUrl}${path}`, {\n method: 'POST',\n headers: {\n 'CENCORI_API_KEY': this.config.apiKey,\n 'Content-Type': 'application/json',\n ...this.config.headers,\n },\n body: JSON.stringify(toBody(request)),\n });\n const data = await response.json();\n if (!response.ok) {\n const message =\n (data && typeof data === 'object' && 'message' in data && typeof data.message === 'string')\n ? data.message\n : `Vision request failed with status ${response.status}`;\n const code =\n (data && typeof data === 'object' && 'error' in data && typeof data.error === 'string')\n ? data.error\n : 'request_failed';\n const err = new Error(message) as Error & { code?: string; details?: unknown };\n err.code = code;\n err.details = data;\n throw err;\n }\n return data as T;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "$schema": "https://json.schemastore.org/package.json",
3
3
  "name": "cencori",
4
- "version": "1.2.1",
4
+ "version": "1.3.1",
5
5
  "description": "Cencori - The unified infrastructure layer for AI applications. One SDK for AI Gateway, Compute, Workflow, and Storage.",
6
6
  "main": "dist/index.js",
7
7
  "module": "dist/index.mjs",
@@ -27,6 +27,16 @@
27
27
  "import": "./dist/ai/index.mjs",
28
28
  "require": "./dist/ai/index.js"
29
29
  },
30
+ "./vision": {
31
+ "types": "./dist/vision/index.d.ts",
32
+ "import": "./dist/vision/index.mjs",
33
+ "require": "./dist/vision/index.js"
34
+ },
35
+ "./react": {
36
+ "types": "./dist/react/index.d.ts",
37
+ "import": "./dist/react/index.mjs",
38
+ "require": "./dist/react/index.js"
39
+ },
30
40
  "./compute": {
31
41
  "types": "./dist/compute/index.d.ts",
32
42
  "import": "./dist/compute/index.mjs",
@@ -87,11 +97,19 @@
87
97
  "prepublishOnly": "npm run build"
88
98
  },
89
99
  "peerDependencies": {
90
- "@ai-sdk/provider": ">=1.0.0"
100
+ "@ai-sdk/provider": ">=1.0.0",
101
+ "react": ">=18.0.0",
102
+ "lucide-react": ">=0.400.0"
91
103
  },
92
104
  "peerDependenciesMeta": {
93
105
  "@ai-sdk/provider": {
94
106
  "optional": true
107
+ },
108
+ "react": {
109
+ "optional": true
110
+ },
111
+ "lucide-react": {
112
+ "optional": true
95
113
  }
96
114
  },
97
115
  "devDependencies": {