@nixxie-cms/ai 1.0.0 → 1.0.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/dist/declarations/src/AnthropicProvider.d.ts +15 -0
- package/dist/declarations/src/AnthropicProvider.d.ts.map +1 -0
- package/dist/declarations/src/OpenAiProvider.d.ts +15 -0
- package/dist/declarations/src/OpenAiProvider.d.ts.map +1 -0
- package/dist/declarations/src/index.d.ts +9 -0
- package/dist/declarations/src/index.d.ts.map +1 -0
- package/dist/declarations/src/types.d.ts +26 -0
- package/dist/declarations/src/types.d.ts.map +1 -0
- package/dist/nixxie-cms-ai.cjs.d.ts +2 -2
- package/dist/nixxie-cms-ai.cjs.js +189 -13
- package/dist/nixxie-cms-ai.esm.js +186 -0
- package/package.json +3 -3
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NixxieAiCompleteOptions, NixxieAiCompletion, NixxieAiMessage, NixxieAiService } from '@nixxie-cms/core';
|
|
2
|
+
import type { AiConfig } from "./types.js";
|
|
3
|
+
/** Anthropic (Claude) provider, backed by the official @anthropic-ai/sdk. */
|
|
4
|
+
export declare class AnthropicProvider implements NixxieAiService {
|
|
5
|
+
private config;
|
|
6
|
+
private model;
|
|
7
|
+
private maxTokens;
|
|
8
|
+
private client;
|
|
9
|
+
constructor(config: AiConfig);
|
|
10
|
+
complete(prompt: string, options?: NixxieAiCompleteOptions): Promise<NixxieAiCompletion>;
|
|
11
|
+
chat(messages: NixxieAiMessage[], options?: NixxieAiCompleteOptions): Promise<NixxieAiCompletion>;
|
|
12
|
+
embed(text: string): Promise<number[]>;
|
|
13
|
+
embedMany(texts: string[]): Promise<number[][]>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=AnthropicProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AnthropicProvider.d.ts","sourceRoot":"../../../src","sources":["AnthropicProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,eAAe,EAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAe;AAcvC,6EAA6E;AAC7E,qBAAa,iBAAkB,YAAW,eAAe;IACvD,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAK;gBAEP,MAAM,EAAE,QAAQ;IAQtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIxF,IAAI,CACR,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAqCxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMtC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;CA6BtD"}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { NixxieAiCompleteOptions, NixxieAiCompletion, NixxieAiMessage, NixxieAiService } from '@nixxie-cms/core';
|
|
2
|
+
import type { AiConfig } from "./types.js";
|
|
3
|
+
/** OpenAI provider, backed by the official openai SDK. */
|
|
4
|
+
export declare class OpenAiProvider implements NixxieAiService {
|
|
5
|
+
private config;
|
|
6
|
+
private model;
|
|
7
|
+
private maxTokens;
|
|
8
|
+
private client;
|
|
9
|
+
constructor(config: AiConfig);
|
|
10
|
+
complete(prompt: string, options?: NixxieAiCompleteOptions): Promise<NixxieAiCompletion>;
|
|
11
|
+
chat(messages: NixxieAiMessage[], options?: NixxieAiCompleteOptions): Promise<NixxieAiCompletion>;
|
|
12
|
+
embed(text: string): Promise<number[]>;
|
|
13
|
+
embedMany(texts: string[]): Promise<number[][]>;
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=OpenAiProvider.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"OpenAiProvider.d.ts","sourceRoot":"../../../src","sources":["OpenAiProvider.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,eAAe,EAChB,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAe;AAavC,0DAA0D;AAC1D,qBAAa,cAAe,YAAW,eAAe;IACpD,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,KAAK,CAAQ;IACrB,OAAO,CAAC,SAAS,CAAQ;IACzB,OAAO,CAAC,MAAM,CAAK;gBAEP,MAAM,EAAE,QAAQ;IAQtB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,uBAAuB,GAAG,OAAO,CAAC,kBAAkB,CAAC;IAIxF,IAAI,CACR,QAAQ,EAAE,eAAe,EAAE,EAC3B,OAAO,CAAC,EAAE,uBAAuB,GAChC,OAAO,CAAC,kBAAkB,CAAC;IAuBxB,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAMtC,SAAS,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;CAetD"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { NixxieAiService } from '@nixxie-cms/core';
|
|
2
|
+
import { AnthropicProvider } from "./AnthropicProvider.js";
|
|
3
|
+
import { OpenAiProvider } from "./OpenAiProvider.js";
|
|
4
|
+
import type { AiConfig } from "./types.js";
|
|
5
|
+
export declare function createAi(config: AiConfig): NixxieAiService;
|
|
6
|
+
export { AnthropicProvider, OpenAiProvider };
|
|
7
|
+
export type { AiConfig, AiProvider } from "./types.js";
|
|
8
|
+
export type { NixxieAiService, NixxieAiMessage, NixxieAiCompleteOptions, NixxieAiCompletion, } from '@nixxie-cms/core';
|
|
9
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"../../../src","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAA;AACvD,OAAO,EAAE,iBAAiB,EAAE,+BAA2B;AACvD,OAAO,EAAE,cAAc,EAAE,4BAAwB;AACjD,OAAO,KAAK,EAAE,QAAQ,EAAE,mBAAe;AAEvC,wBAAgB,QAAQ,CAAC,MAAM,EAAE,QAAQ,GAAG,eAAe,CAW1D;AAED,OAAO,EAAE,iBAAiB,EAAE,cAAc,EAAE,CAAA;AAC5C,YAAY,EAAE,QAAQ,EAAE,UAAU,EAAE,mBAAe;AACnD,YAAY,EACV,eAAe,EACf,eAAe,EACf,uBAAuB,EACvB,kBAAkB,GACnB,MAAM,kBAAkB,CAAA"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { NixxieAiCompleteOptions, NixxieAiCompletion, NixxieAiMessage, NixxieAiService } from '@nixxie-cms/core';
|
|
2
|
+
export type { NixxieAiCompleteOptions, NixxieAiCompletion, NixxieAiMessage, NixxieAiService };
|
|
3
|
+
export type AiProvider = 'anthropic' | 'openai';
|
|
4
|
+
export type AiConfig = {
|
|
5
|
+
/** LLM provider. Default: 'anthropic'. */
|
|
6
|
+
provider?: AiProvider;
|
|
7
|
+
/** API key for the provider. */
|
|
8
|
+
apiKey: string;
|
|
9
|
+
/**
|
|
10
|
+
* Default model for completions. Defaults to 'claude-opus-4-8' (Anthropic) or 'gpt-4o' (OpenAI).
|
|
11
|
+
*/
|
|
12
|
+
model?: string;
|
|
13
|
+
/** Override the API base URL (proxies, gateways, Azure, etc.). */
|
|
14
|
+
baseUrl?: string;
|
|
15
|
+
/** Default max output tokens. Default: 4096. */
|
|
16
|
+
maxTokens?: number;
|
|
17
|
+
/**
|
|
18
|
+
* Model used for `embed`/`embedMany`. Anthropic has no native embeddings endpoint, so for the
|
|
19
|
+
* Anthropic provider embeddings are produced via Voyage AI (set `voyageApiKey`). Defaults to
|
|
20
|
+
* 'voyage-3' (Voyage) or 'text-embedding-3-small' (OpenAI).
|
|
21
|
+
*/
|
|
22
|
+
embeddingModel?: string;
|
|
23
|
+
/** Voyage AI key, used for embeddings when `provider` is 'anthropic'. */
|
|
24
|
+
voyageApiKey?: string;
|
|
25
|
+
};
|
|
26
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"../../../src","sources":["types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,uBAAuB,EACvB,kBAAkB,EAClB,eAAe,EACf,eAAe,EAChB,MAAM,kBAAkB,CAAA;AAEzB,YAAY,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,eAAe,EAAE,eAAe,EAAE,CAAA;AAE7F,MAAM,MAAM,UAAU,GAAG,WAAW,GAAG,QAAQ,CAAA;AAE/C,MAAM,MAAM,QAAQ,GAAG;IACrB,0CAA0C;IAC1C,QAAQ,CAAC,EAAE,UAAU,CAAA;IACrB,gCAAgC;IAChC,MAAM,EAAE,MAAM,CAAA;IACd;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,kEAAkE;IAClE,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,gDAAgD;IAChD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;;OAIG;IACH,cAAc,CAAC,EAAE,MAAM,CAAA;IACvB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAA;CACtB,CAAA"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from "
|
|
2
|
-
//# sourceMappingURL=data:application/json;charset=utf-8;base64,
|
|
1
|
+
export * from "./declarations/src/index.js";
|
|
2
|
+
//# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibml4eGllLWNtcy1haS5janMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4vZGVjbGFyYXRpb25zL3NyYy9pbmRleC5kLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBIn0=
|
|
@@ -1,16 +1,192 @@
|
|
|
1
|
-
|
|
2
|
-
// this file might look strange and you might be wondering what it's for
|
|
3
|
-
// it's lets you import your source files by importing this entrypoint
|
|
4
|
-
// as you would import it if it was built with preconstruct build
|
|
5
|
-
// this file is slightly different to some others though
|
|
6
|
-
// it has a require hook which compiles your code with Babel
|
|
7
|
-
// this means that you don't have to set up @babel/register or anything like that
|
|
8
|
-
// but you can still require this module and it'll be compiled
|
|
1
|
+
'use strict';
|
|
9
2
|
|
|
10
|
-
|
|
11
|
-
let unregister = require("../../../node_modules/.pnpm/@preconstruct+hook@0.4.0/node_modules/@preconstruct/hook").___internalHook(typeof __dirname === 'undefined' ? undefined : __dirname, "../../..", "..");
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
12
4
|
|
|
13
|
-
|
|
14
|
-
|
|
5
|
+
const DEFAULT_MODEL$1 = 'claude-opus-4-8';
|
|
6
|
+
function loadAnthropic() {
|
|
7
|
+
try {
|
|
8
|
+
var _require$default;
|
|
9
|
+
return (_require$default = require('@anthropic-ai/sdk').default) !== null && _require$default !== void 0 ? _require$default : require('@anthropic-ai/sdk');
|
|
10
|
+
} catch {
|
|
11
|
+
throw new Error('The Anthropic provider requires @anthropic-ai/sdk. Run: npm install @anthropic-ai/sdk');
|
|
12
|
+
}
|
|
13
|
+
}
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
/** Anthropic (Claude) provider, backed by the official @anthropic-ai/sdk. */
|
|
16
|
+
class AnthropicProvider {
|
|
17
|
+
constructor(config) {
|
|
18
|
+
var _config$model, _config$maxTokens;
|
|
19
|
+
this.config = config;
|
|
20
|
+
this.model = (_config$model = config.model) !== null && _config$model !== void 0 ? _config$model : DEFAULT_MODEL$1;
|
|
21
|
+
this.maxTokens = (_config$maxTokens = config.maxTokens) !== null && _config$maxTokens !== void 0 ? _config$maxTokens : 4096;
|
|
22
|
+
const Anthropic = loadAnthropic();
|
|
23
|
+
this.client = new Anthropic({
|
|
24
|
+
apiKey: config.apiKey,
|
|
25
|
+
baseURL: config.baseUrl
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
async complete(prompt, options) {
|
|
29
|
+
return this.chat([{
|
|
30
|
+
role: 'user',
|
|
31
|
+
content: prompt
|
|
32
|
+
}], options);
|
|
33
|
+
}
|
|
34
|
+
async chat(messages, options) {
|
|
35
|
+
var _options$model, _options$maxTokens, _response$content, _response$model, _response$usage, _response$usage2;
|
|
36
|
+
// Anthropic takes the system prompt as a top-level field, not a message.
|
|
37
|
+
const system = [options === null || options === void 0 ? void 0 : options.system, ...messages.filter(m => m.role === 'system').map(m => m.content)].filter(Boolean).join('\n\n');
|
|
38
|
+
const turns = messages.filter(m => m.role !== 'system').map(m => ({
|
|
39
|
+
role: m.role,
|
|
40
|
+
content: m.content
|
|
41
|
+
}));
|
|
42
|
+
const model = (_options$model = options === null || options === void 0 ? void 0 : options.model) !== null && _options$model !== void 0 ? _options$model : this.model;
|
|
43
|
+
const response = await this.client.messages.create({
|
|
44
|
+
model,
|
|
45
|
+
max_tokens: (_options$maxTokens = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _options$maxTokens !== void 0 ? _options$maxTokens : this.maxTokens,
|
|
46
|
+
system: system || undefined,
|
|
47
|
+
messages: turns,
|
|
48
|
+
...((options === null || options === void 0 ? void 0 : options.temperature) !== undefined ? {
|
|
49
|
+
temperature: options.temperature
|
|
50
|
+
} : {})
|
|
51
|
+
});
|
|
52
|
+
const text = ((_response$content = response.content) !== null && _response$content !== void 0 ? _response$content : []).filter(block => block.type === 'text').map(block => block.text).join('');
|
|
53
|
+
return {
|
|
54
|
+
text,
|
|
55
|
+
model: (_response$model = response.model) !== null && _response$model !== void 0 ? _response$model : model,
|
|
56
|
+
usage: {
|
|
57
|
+
inputTokens: (_response$usage = response.usage) === null || _response$usage === void 0 ? void 0 : _response$usage.input_tokens,
|
|
58
|
+
outputTokens: (_response$usage2 = response.usage) === null || _response$usage2 === void 0 ? void 0 : _response$usage2.output_tokens
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
async embed(text) {
|
|
63
|
+
const [vector] = await this.embedMany([text]);
|
|
64
|
+
if (!vector) throw new Error('Embedding provider returned no vector');
|
|
65
|
+
return vector;
|
|
66
|
+
}
|
|
67
|
+
async embedMany(texts) {
|
|
68
|
+
var _this$config$embeddin, _data$data;
|
|
69
|
+
if (!this.config.voyageApiKey) {
|
|
70
|
+
throw new Error('Anthropic has no native embeddings endpoint. Set `voyageApiKey` to generate embeddings via Voyage AI, or use the OpenAI provider.');
|
|
71
|
+
}
|
|
72
|
+
const model = (_this$config$embeddin = this.config.embeddingModel) !== null && _this$config$embeddin !== void 0 ? _this$config$embeddin : 'voyage-3';
|
|
73
|
+
const res = await fetch('https://api.voyageai.com/v1/embeddings', {
|
|
74
|
+
method: 'POST',
|
|
75
|
+
headers: {
|
|
76
|
+
'Content-Type': 'application/json',
|
|
77
|
+
Authorization: `Bearer ${this.config.voyageApiKey}`
|
|
78
|
+
},
|
|
79
|
+
body: JSON.stringify({
|
|
80
|
+
input: texts,
|
|
81
|
+
model
|
|
82
|
+
})
|
|
83
|
+
});
|
|
84
|
+
if (!res.ok) throw new Error(`Voyage embeddings failed (${res.status}): ${await res.text()}`);
|
|
85
|
+
const data = await res.json();
|
|
86
|
+
const items = (_data$data = data.data) !== null && _data$data !== void 0 ? _data$data : [];
|
|
87
|
+
if (items.length !== texts.length) {
|
|
88
|
+
throw new Error(`Voyage returned ${items.length} embeddings for ${texts.length} inputs`);
|
|
89
|
+
}
|
|
90
|
+
// The API does not guarantee response order matches input order — sort by `index`.
|
|
91
|
+
return items.slice().sort((a, b) => {
|
|
92
|
+
var _a$index, _b$index;
|
|
93
|
+
return ((_a$index = a.index) !== null && _a$index !== void 0 ? _a$index : 0) - ((_b$index = b.index) !== null && _b$index !== void 0 ? _b$index : 0);
|
|
94
|
+
}).map(d => d.embedding);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const DEFAULT_MODEL = 'gpt-4o';
|
|
99
|
+
const DEFAULT_EMBEDDING_MODEL = 'text-embedding-3-small';
|
|
100
|
+
function loadOpenAi() {
|
|
101
|
+
try {
|
|
102
|
+
var _require$default;
|
|
103
|
+
return (_require$default = require('openai').default) !== null && _require$default !== void 0 ? _require$default : require('openai');
|
|
104
|
+
} catch {
|
|
105
|
+
throw new Error('The OpenAI provider requires the openai package. Run: npm install openai');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** OpenAI provider, backed by the official openai SDK. */
|
|
110
|
+
class OpenAiProvider {
|
|
111
|
+
constructor(config) {
|
|
112
|
+
var _config$model, _config$maxTokens;
|
|
113
|
+
this.config = config;
|
|
114
|
+
this.model = (_config$model = config.model) !== null && _config$model !== void 0 ? _config$model : DEFAULT_MODEL;
|
|
115
|
+
this.maxTokens = (_config$maxTokens = config.maxTokens) !== null && _config$maxTokens !== void 0 ? _config$maxTokens : 4096;
|
|
116
|
+
const OpenAI = loadOpenAi();
|
|
117
|
+
this.client = new OpenAI({
|
|
118
|
+
apiKey: config.apiKey,
|
|
119
|
+
baseURL: config.baseUrl
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
async complete(prompt, options) {
|
|
123
|
+
return this.chat([{
|
|
124
|
+
role: 'user',
|
|
125
|
+
content: prompt
|
|
126
|
+
}], options);
|
|
127
|
+
}
|
|
128
|
+
async chat(messages, options) {
|
|
129
|
+
var _options$model, _options$maxTokens, _response$choices$0$m, _response$choices, _response$model, _response$usage, _response$usage2;
|
|
130
|
+
const allMessages = options !== null && options !== void 0 && options.system ? [{
|
|
131
|
+
role: 'system',
|
|
132
|
+
content: options.system
|
|
133
|
+
}, ...messages] : messages;
|
|
134
|
+
const model = (_options$model = options === null || options === void 0 ? void 0 : options.model) !== null && _options$model !== void 0 ? _options$model : this.model;
|
|
135
|
+
const response = await this.client.chat.completions.create({
|
|
136
|
+
model,
|
|
137
|
+
max_tokens: (_options$maxTokens = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _options$maxTokens !== void 0 ? _options$maxTokens : this.maxTokens,
|
|
138
|
+
messages: allMessages,
|
|
139
|
+
...((options === null || options === void 0 ? void 0 : options.temperature) !== undefined ? {
|
|
140
|
+
temperature: options.temperature
|
|
141
|
+
} : {})
|
|
142
|
+
});
|
|
143
|
+
return {
|
|
144
|
+
text: (_response$choices$0$m = (_response$choices = response.choices) === null || _response$choices === void 0 || (_response$choices = _response$choices[0]) === null || _response$choices === void 0 || (_response$choices = _response$choices.message) === null || _response$choices === void 0 ? void 0 : _response$choices.content) !== null && _response$choices$0$m !== void 0 ? _response$choices$0$m : '',
|
|
145
|
+
model: (_response$model = response.model) !== null && _response$model !== void 0 ? _response$model : model,
|
|
146
|
+
usage: {
|
|
147
|
+
inputTokens: (_response$usage = response.usage) === null || _response$usage === void 0 ? void 0 : _response$usage.prompt_tokens,
|
|
148
|
+
outputTokens: (_response$usage2 = response.usage) === null || _response$usage2 === void 0 ? void 0 : _response$usage2.completion_tokens
|
|
149
|
+
}
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
async embed(text) {
|
|
153
|
+
const [vector] = await this.embedMany([text]);
|
|
154
|
+
if (!vector) throw new Error('Embedding provider returned no vector');
|
|
155
|
+
return vector;
|
|
156
|
+
}
|
|
157
|
+
async embedMany(texts) {
|
|
158
|
+
var _this$config$embeddin, _response$data;
|
|
159
|
+
const response = await this.client.embeddings.create({
|
|
160
|
+
model: (_this$config$embeddin = this.config.embeddingModel) !== null && _this$config$embeddin !== void 0 ? _this$config$embeddin : DEFAULT_EMBEDDING_MODEL,
|
|
161
|
+
input: texts
|
|
162
|
+
});
|
|
163
|
+
const items = (_response$data = response.data) !== null && _response$data !== void 0 ? _response$data : [];
|
|
164
|
+
if (items.length !== texts.length) {
|
|
165
|
+
throw new Error(`OpenAI returned ${items.length} embeddings for ${texts.length} inputs`);
|
|
166
|
+
}
|
|
167
|
+
// The API does not guarantee response order matches input order — sort by `index`.
|
|
168
|
+
return items.slice().sort((a, b) => {
|
|
169
|
+
var _a$index, _b$index;
|
|
170
|
+
return ((_a$index = a.index) !== null && _a$index !== void 0 ? _a$index : 0) - ((_b$index = b.index) !== null && _b$index !== void 0 ? _b$index : 0);
|
|
171
|
+
}).map(d => d.embedding);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function createAi(config) {
|
|
176
|
+
var _config$provider;
|
|
177
|
+
switch ((_config$provider = config.provider) !== null && _config$provider !== void 0 ? _config$provider : 'anthropic') {
|
|
178
|
+
case 'anthropic':
|
|
179
|
+
return new AnthropicProvider(config);
|
|
180
|
+
case 'openai':
|
|
181
|
+
return new OpenAiProvider(config);
|
|
182
|
+
default:
|
|
183
|
+
{
|
|
184
|
+
const exhaustive = config.provider;
|
|
185
|
+
throw new Error(`Unknown AI provider: ${exhaustive}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
exports.AnthropicProvider = AnthropicProvider;
|
|
191
|
+
exports.OpenAiProvider = OpenAiProvider;
|
|
192
|
+
exports.createAi = createAi;
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
const DEFAULT_MODEL$1 = 'claude-opus-4-8';
|
|
2
|
+
function loadAnthropic() {
|
|
3
|
+
try {
|
|
4
|
+
var _require$default;
|
|
5
|
+
return (_require$default = require('@anthropic-ai/sdk').default) !== null && _require$default !== void 0 ? _require$default : require('@anthropic-ai/sdk');
|
|
6
|
+
} catch {
|
|
7
|
+
throw new Error('The Anthropic provider requires @anthropic-ai/sdk. Run: npm install @anthropic-ai/sdk');
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
/** Anthropic (Claude) provider, backed by the official @anthropic-ai/sdk. */
|
|
12
|
+
class AnthropicProvider {
|
|
13
|
+
constructor(config) {
|
|
14
|
+
var _config$model, _config$maxTokens;
|
|
15
|
+
this.config = config;
|
|
16
|
+
this.model = (_config$model = config.model) !== null && _config$model !== void 0 ? _config$model : DEFAULT_MODEL$1;
|
|
17
|
+
this.maxTokens = (_config$maxTokens = config.maxTokens) !== null && _config$maxTokens !== void 0 ? _config$maxTokens : 4096;
|
|
18
|
+
const Anthropic = loadAnthropic();
|
|
19
|
+
this.client = new Anthropic({
|
|
20
|
+
apiKey: config.apiKey,
|
|
21
|
+
baseURL: config.baseUrl
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
async complete(prompt, options) {
|
|
25
|
+
return this.chat([{
|
|
26
|
+
role: 'user',
|
|
27
|
+
content: prompt
|
|
28
|
+
}], options);
|
|
29
|
+
}
|
|
30
|
+
async chat(messages, options) {
|
|
31
|
+
var _options$model, _options$maxTokens, _response$content, _response$model, _response$usage, _response$usage2;
|
|
32
|
+
// Anthropic takes the system prompt as a top-level field, not a message.
|
|
33
|
+
const system = [options === null || options === void 0 ? void 0 : options.system, ...messages.filter(m => m.role === 'system').map(m => m.content)].filter(Boolean).join('\n\n');
|
|
34
|
+
const turns = messages.filter(m => m.role !== 'system').map(m => ({
|
|
35
|
+
role: m.role,
|
|
36
|
+
content: m.content
|
|
37
|
+
}));
|
|
38
|
+
const model = (_options$model = options === null || options === void 0 ? void 0 : options.model) !== null && _options$model !== void 0 ? _options$model : this.model;
|
|
39
|
+
const response = await this.client.messages.create({
|
|
40
|
+
model,
|
|
41
|
+
max_tokens: (_options$maxTokens = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _options$maxTokens !== void 0 ? _options$maxTokens : this.maxTokens,
|
|
42
|
+
system: system || undefined,
|
|
43
|
+
messages: turns,
|
|
44
|
+
...((options === null || options === void 0 ? void 0 : options.temperature) !== undefined ? {
|
|
45
|
+
temperature: options.temperature
|
|
46
|
+
} : {})
|
|
47
|
+
});
|
|
48
|
+
const text = ((_response$content = response.content) !== null && _response$content !== void 0 ? _response$content : []).filter(block => block.type === 'text').map(block => block.text).join('');
|
|
49
|
+
return {
|
|
50
|
+
text,
|
|
51
|
+
model: (_response$model = response.model) !== null && _response$model !== void 0 ? _response$model : model,
|
|
52
|
+
usage: {
|
|
53
|
+
inputTokens: (_response$usage = response.usage) === null || _response$usage === void 0 ? void 0 : _response$usage.input_tokens,
|
|
54
|
+
outputTokens: (_response$usage2 = response.usage) === null || _response$usage2 === void 0 ? void 0 : _response$usage2.output_tokens
|
|
55
|
+
}
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async embed(text) {
|
|
59
|
+
const [vector] = await this.embedMany([text]);
|
|
60
|
+
if (!vector) throw new Error('Embedding provider returned no vector');
|
|
61
|
+
return vector;
|
|
62
|
+
}
|
|
63
|
+
async embedMany(texts) {
|
|
64
|
+
var _this$config$embeddin, _data$data;
|
|
65
|
+
if (!this.config.voyageApiKey) {
|
|
66
|
+
throw new Error('Anthropic has no native embeddings endpoint. Set `voyageApiKey` to generate embeddings via Voyage AI, or use the OpenAI provider.');
|
|
67
|
+
}
|
|
68
|
+
const model = (_this$config$embeddin = this.config.embeddingModel) !== null && _this$config$embeddin !== void 0 ? _this$config$embeddin : 'voyage-3';
|
|
69
|
+
const res = await fetch('https://api.voyageai.com/v1/embeddings', {
|
|
70
|
+
method: 'POST',
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
Authorization: `Bearer ${this.config.voyageApiKey}`
|
|
74
|
+
},
|
|
75
|
+
body: JSON.stringify({
|
|
76
|
+
input: texts,
|
|
77
|
+
model
|
|
78
|
+
})
|
|
79
|
+
});
|
|
80
|
+
if (!res.ok) throw new Error(`Voyage embeddings failed (${res.status}): ${await res.text()}`);
|
|
81
|
+
const data = await res.json();
|
|
82
|
+
const items = (_data$data = data.data) !== null && _data$data !== void 0 ? _data$data : [];
|
|
83
|
+
if (items.length !== texts.length) {
|
|
84
|
+
throw new Error(`Voyage returned ${items.length} embeddings for ${texts.length} inputs`);
|
|
85
|
+
}
|
|
86
|
+
// The API does not guarantee response order matches input order — sort by `index`.
|
|
87
|
+
return items.slice().sort((a, b) => {
|
|
88
|
+
var _a$index, _b$index;
|
|
89
|
+
return ((_a$index = a.index) !== null && _a$index !== void 0 ? _a$index : 0) - ((_b$index = b.index) !== null && _b$index !== void 0 ? _b$index : 0);
|
|
90
|
+
}).map(d => d.embedding);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const DEFAULT_MODEL = 'gpt-4o';
|
|
95
|
+
const DEFAULT_EMBEDDING_MODEL = 'text-embedding-3-small';
|
|
96
|
+
function loadOpenAi() {
|
|
97
|
+
try {
|
|
98
|
+
var _require$default;
|
|
99
|
+
return (_require$default = require('openai').default) !== null && _require$default !== void 0 ? _require$default : require('openai');
|
|
100
|
+
} catch {
|
|
101
|
+
throw new Error('The OpenAI provider requires the openai package. Run: npm install openai');
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** OpenAI provider, backed by the official openai SDK. */
|
|
106
|
+
class OpenAiProvider {
|
|
107
|
+
constructor(config) {
|
|
108
|
+
var _config$model, _config$maxTokens;
|
|
109
|
+
this.config = config;
|
|
110
|
+
this.model = (_config$model = config.model) !== null && _config$model !== void 0 ? _config$model : DEFAULT_MODEL;
|
|
111
|
+
this.maxTokens = (_config$maxTokens = config.maxTokens) !== null && _config$maxTokens !== void 0 ? _config$maxTokens : 4096;
|
|
112
|
+
const OpenAI = loadOpenAi();
|
|
113
|
+
this.client = new OpenAI({
|
|
114
|
+
apiKey: config.apiKey,
|
|
115
|
+
baseURL: config.baseUrl
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async complete(prompt, options) {
|
|
119
|
+
return this.chat([{
|
|
120
|
+
role: 'user',
|
|
121
|
+
content: prompt
|
|
122
|
+
}], options);
|
|
123
|
+
}
|
|
124
|
+
async chat(messages, options) {
|
|
125
|
+
var _options$model, _options$maxTokens, _response$choices$0$m, _response$choices, _response$model, _response$usage, _response$usage2;
|
|
126
|
+
const allMessages = options !== null && options !== void 0 && options.system ? [{
|
|
127
|
+
role: 'system',
|
|
128
|
+
content: options.system
|
|
129
|
+
}, ...messages] : messages;
|
|
130
|
+
const model = (_options$model = options === null || options === void 0 ? void 0 : options.model) !== null && _options$model !== void 0 ? _options$model : this.model;
|
|
131
|
+
const response = await this.client.chat.completions.create({
|
|
132
|
+
model,
|
|
133
|
+
max_tokens: (_options$maxTokens = options === null || options === void 0 ? void 0 : options.maxTokens) !== null && _options$maxTokens !== void 0 ? _options$maxTokens : this.maxTokens,
|
|
134
|
+
messages: allMessages,
|
|
135
|
+
...((options === null || options === void 0 ? void 0 : options.temperature) !== undefined ? {
|
|
136
|
+
temperature: options.temperature
|
|
137
|
+
} : {})
|
|
138
|
+
});
|
|
139
|
+
return {
|
|
140
|
+
text: (_response$choices$0$m = (_response$choices = response.choices) === null || _response$choices === void 0 || (_response$choices = _response$choices[0]) === null || _response$choices === void 0 || (_response$choices = _response$choices.message) === null || _response$choices === void 0 ? void 0 : _response$choices.content) !== null && _response$choices$0$m !== void 0 ? _response$choices$0$m : '',
|
|
141
|
+
model: (_response$model = response.model) !== null && _response$model !== void 0 ? _response$model : model,
|
|
142
|
+
usage: {
|
|
143
|
+
inputTokens: (_response$usage = response.usage) === null || _response$usage === void 0 ? void 0 : _response$usage.prompt_tokens,
|
|
144
|
+
outputTokens: (_response$usage2 = response.usage) === null || _response$usage2 === void 0 ? void 0 : _response$usage2.completion_tokens
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
async embed(text) {
|
|
149
|
+
const [vector] = await this.embedMany([text]);
|
|
150
|
+
if (!vector) throw new Error('Embedding provider returned no vector');
|
|
151
|
+
return vector;
|
|
152
|
+
}
|
|
153
|
+
async embedMany(texts) {
|
|
154
|
+
var _this$config$embeddin, _response$data;
|
|
155
|
+
const response = await this.client.embeddings.create({
|
|
156
|
+
model: (_this$config$embeddin = this.config.embeddingModel) !== null && _this$config$embeddin !== void 0 ? _this$config$embeddin : DEFAULT_EMBEDDING_MODEL,
|
|
157
|
+
input: texts
|
|
158
|
+
});
|
|
159
|
+
const items = (_response$data = response.data) !== null && _response$data !== void 0 ? _response$data : [];
|
|
160
|
+
if (items.length !== texts.length) {
|
|
161
|
+
throw new Error(`OpenAI returned ${items.length} embeddings for ${texts.length} inputs`);
|
|
162
|
+
}
|
|
163
|
+
// The API does not guarantee response order matches input order — sort by `index`.
|
|
164
|
+
return items.slice().sort((a, b) => {
|
|
165
|
+
var _a$index, _b$index;
|
|
166
|
+
return ((_a$index = a.index) !== null && _a$index !== void 0 ? _a$index : 0) - ((_b$index = b.index) !== null && _b$index !== void 0 ? _b$index : 0);
|
|
167
|
+
}).map(d => d.embedding);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function createAi(config) {
|
|
172
|
+
var _config$provider;
|
|
173
|
+
switch ((_config$provider = config.provider) !== null && _config$provider !== void 0 ? _config$provider : 'anthropic') {
|
|
174
|
+
case 'anthropic':
|
|
175
|
+
return new AnthropicProvider(config);
|
|
176
|
+
case 'openai':
|
|
177
|
+
return new OpenAiProvider(config);
|
|
178
|
+
default:
|
|
179
|
+
{
|
|
180
|
+
const exhaustive = config.provider;
|
|
181
|
+
throw new Error(`Unknown AI provider: ${exhaustive}`);
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export { AnthropicProvider, OpenAiProvider, createAi };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@nixxie-cms/ai",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/nixxie-cms-ai.cjs.js",
|
|
6
6
|
"module": "dist/nixxie-cms-ai.esm.js",
|
|
@@ -16,10 +16,10 @@
|
|
|
16
16
|
"@babel/runtime": "^7.24.7"
|
|
17
17
|
},
|
|
18
18
|
"devDependencies": {
|
|
19
|
-
"@nixxie-cms/core": "^1.0.
|
|
19
|
+
"@nixxie-cms/core": "^1.0.1"
|
|
20
20
|
},
|
|
21
21
|
"peerDependencies": {
|
|
22
|
-
"@nixxie-cms/core": "^1.0.
|
|
22
|
+
"@nixxie-cms/core": "^1.0.1"
|
|
23
23
|
},
|
|
24
24
|
"optionalDependencies": {
|
|
25
25
|
"@anthropic-ai/sdk": "^0.69.0",
|