@animalabs/membrane 0.1.19 → 0.2.0
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/providers/index.d.ts +1 -0
- package/dist/providers/index.d.ts.map +1 -1
- package/dist/providers/index.js +1 -0
- package/dist/providers/index.js.map +1 -1
- package/dist/providers/openai-completions.d.ts +62 -0
- package/dist/providers/openai-completions.d.ts.map +1 -0
- package/dist/providers/openai-completions.js +286 -0
- package/dist/providers/openai-completions.js.map +1 -0
- package/package.json +1 -1
- package/src/providers/index.ts +5 -0
- package/src/providers/openai-completions.ts +429 -0
|
@@ -5,4 +5,5 @@ export { AnthropicAdapter, toAnthropicContent, fromAnthropicContent, type Anthro
|
|
|
5
5
|
export { OpenRouterAdapter, toOpenRouterMessages, fromOpenRouterMessage, type OpenRouterAdapterConfig, } from './openrouter.js';
|
|
6
6
|
export { OpenAIAdapter, toOpenAIContent, fromOpenAIContent, type OpenAIAdapterConfig, } from './openai.js';
|
|
7
7
|
export { OpenAICompatibleAdapter, toOpenAIMessages, fromOpenAIMessage, type OpenAICompatibleAdapterConfig, } from './openai-compatible.js';
|
|
8
|
+
export { OpenAICompletionsAdapter, type OpenAICompletionsAdapterConfig, } from './openai-completions.js';
|
|
8
9
|
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,EACpB,KAAK,sBAAsB,GAC5B,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,EACrB,KAAK,uBAAuB,GAC7B,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,KAAK,mBAAmB,GACzB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,EACjB,KAAK,6BAA6B,GACnC,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,wBAAwB,EACxB,KAAK,8BAA8B,GACpC,MAAM,yBAAyB,CAAC"}
|
package/dist/providers/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export { AnthropicAdapter, toAnthropicContent, fromAnthropicContent, } from './a
|
|
|
5
5
|
export { OpenRouterAdapter, toOpenRouterMessages, fromOpenRouterMessage, } from './openrouter.js';
|
|
6
6
|
export { OpenAIAdapter, toOpenAIContent, fromOpenAIContent, } from './openai.js';
|
|
7
7
|
export { OpenAICompatibleAdapter, toOpenAIMessages, fromOpenAIMessage, } from './openai-compatible.js';
|
|
8
|
+
export { OpenAICompletionsAdapter, } from './openai-completions.js';
|
|
8
9
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GAErB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,GAElB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,wBAAwB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,oBAAoB,GAErB,MAAM,gBAAgB,CAAC;AAExB,OAAO,EACL,iBAAiB,EACjB,oBAAoB,EACpB,qBAAqB,GAEtB,MAAM,iBAAiB,CAAC;AAEzB,OAAO,EACL,aAAa,EACb,eAAe,EACf,iBAAiB,GAElB,MAAM,aAAa,CAAC;AAErB,OAAO,EACL,uBAAuB,EACvB,gBAAgB,EAChB,iBAAiB,GAElB,MAAM,wBAAwB,CAAC;AAEhC,OAAO,EACL,wBAAwB,GAEzB,MAAM,yBAAyB,CAAC"}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-Compatible Completions adapter for base models
|
|
3
|
+
*
|
|
4
|
+
* For true base/completion models that use the `/v1/completions` endpoint:
|
|
5
|
+
* - No chat formatting built-in
|
|
6
|
+
* - Single text prompt input
|
|
7
|
+
* - Raw completion output
|
|
8
|
+
* - No image support
|
|
9
|
+
*
|
|
10
|
+
* Serializes conversations to Human:/Assistant: format.
|
|
11
|
+
*/
|
|
12
|
+
import type { ProviderAdapter, ProviderRequest, ProviderRequestOptions, ProviderResponse, StreamCallbacks } from '../types/index.js';
|
|
13
|
+
export interface OpenAICompletionsAdapterConfig {
|
|
14
|
+
/** Base URL for the API (required, e.g., 'http://localhost:8000/v1') */
|
|
15
|
+
baseURL: string;
|
|
16
|
+
/** API key (optional for local servers) */
|
|
17
|
+
apiKey?: string;
|
|
18
|
+
/** Provider name for logging/identification (default: 'openai-completions') */
|
|
19
|
+
providerName?: string;
|
|
20
|
+
/** Default max tokens */
|
|
21
|
+
defaultMaxTokens?: number;
|
|
22
|
+
/** Additional headers to include with requests */
|
|
23
|
+
extraHeaders?: Record<string, string>;
|
|
24
|
+
/**
|
|
25
|
+
* Stop sequences to use (default: ['\n\nHuman:', '\nHuman:'])
|
|
26
|
+
* These prevent the model from generating user turns.
|
|
27
|
+
*/
|
|
28
|
+
defaultStopSequences?: string[];
|
|
29
|
+
/**
|
|
30
|
+
* Whether to warn when images are stripped from context (default: true)
|
|
31
|
+
*/
|
|
32
|
+
warnOnImageStrip?: boolean;
|
|
33
|
+
}
|
|
34
|
+
export declare class OpenAICompletionsAdapter implements ProviderAdapter {
|
|
35
|
+
readonly name: string;
|
|
36
|
+
private baseURL;
|
|
37
|
+
private apiKey;
|
|
38
|
+
private defaultMaxTokens;
|
|
39
|
+
private extraHeaders;
|
|
40
|
+
private defaultStopSequences;
|
|
41
|
+
private warnOnImageStrip;
|
|
42
|
+
constructor(config: OpenAICompletionsAdapterConfig);
|
|
43
|
+
supportsModel(_modelId: string): boolean;
|
|
44
|
+
complete(request: ProviderRequest, options?: ProviderRequestOptions): Promise<ProviderResponse>;
|
|
45
|
+
stream(request: ProviderRequest, callbacks: StreamCallbacks, options?: ProviderRequestOptions): Promise<ProviderResponse>;
|
|
46
|
+
/**
|
|
47
|
+
* Serialize messages to Human:/Assistant: format for base models.
|
|
48
|
+
* Images are stripped from content.
|
|
49
|
+
*/
|
|
50
|
+
serializeToPrompt(messages: any[]): string;
|
|
51
|
+
private normalizeRole;
|
|
52
|
+
private extractTextContent;
|
|
53
|
+
private getHeaders;
|
|
54
|
+
private buildRequest;
|
|
55
|
+
private makeRequest;
|
|
56
|
+
private parseResponse;
|
|
57
|
+
private buildStreamedResponse;
|
|
58
|
+
private textToContent;
|
|
59
|
+
private mapFinishReason;
|
|
60
|
+
private handleError;
|
|
61
|
+
}
|
|
62
|
+
//# sourceMappingURL=openai-completions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-completions.d.ts","sourceRoot":"","sources":["../../src/providers/openai-completions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EACf,sBAAsB,EACtB,gBAAgB,EAChB,eAAe,EAEhB,MAAM,mBAAmB,CAAC;AA2C3B,MAAM,WAAW,8BAA8B;IAC7C,wEAAwE;IACxE,OAAO,EAAE,MAAM,CAAC;IAEhB,2CAA2C;IAC3C,MAAM,CAAC,EAAE,MAAM,CAAC;IAEhB,+EAA+E;IAC/E,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,yBAAyB;IACzB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAE1B,kDAAkD;IAClD,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAEtC;;;OAGG;IACH,oBAAoB,CAAC,EAAE,MAAM,EAAE,CAAC;IAEhC;;OAEG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAMD,qBAAa,wBAAyB,YAAW,eAAe;IAC9D,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,gBAAgB,CAAS;IACjC,OAAO,CAAC,YAAY,CAAyB;IAC7C,OAAO,CAAC,oBAAoB,CAAW;IACvC,OAAO,CAAC,gBAAgB,CAAU;gBAEtB,MAAM,EAAE,8BAA8B;IAclD,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO;IAIlC,QAAQ,CACZ,OAAO,EAAE,eAAe,EACxB,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;IAWtB,MAAM,CACV,OAAO,EAAE,eAAe,EACxB,SAAS,EAAE,eAAe,EAC1B,OAAO,CAAC,EAAE,sBAAsB,GAC/B,OAAO,CAAC,gBAAgB,CAAC;IAkE5B;;;OAGG;IACH,iBAAiB,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM;IA6B1C,OAAO,CAAC,aAAa;IAOrB,OAAO,CAAC,kBAAkB;IA4B1B,OAAO,CAAC,UAAU;IAalB,OAAO,CAAC,YAAY;YA+BN,WAAW;IAgBzB,OAAO,CAAC,aAAa;IAkBrB,OAAO,CAAC,qBAAqB;IAoB7B,OAAO,CAAC,aAAa;IASrB,OAAO,CAAC,eAAe;IAWvB,OAAO,CAAC,WAAW;CAqCpB"}
|
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-Compatible Completions adapter for base models
|
|
3
|
+
*
|
|
4
|
+
* For true base/completion models that use the `/v1/completions` endpoint:
|
|
5
|
+
* - No chat formatting built-in
|
|
6
|
+
* - Single text prompt input
|
|
7
|
+
* - Raw completion output
|
|
8
|
+
* - No image support
|
|
9
|
+
*
|
|
10
|
+
* Serializes conversations to Human:/Assistant: format.
|
|
11
|
+
*/
|
|
12
|
+
import { MembraneError, rateLimitError, contextLengthError, authError, serverError, abortError, networkError, } from '../types/index.js';
|
|
13
|
+
// ============================================================================
|
|
14
|
+
// OpenAI Completions Adapter
|
|
15
|
+
// ============================================================================
|
|
16
|
+
export class OpenAICompletionsAdapter {
|
|
17
|
+
name;
|
|
18
|
+
baseURL;
|
|
19
|
+
apiKey;
|
|
20
|
+
defaultMaxTokens;
|
|
21
|
+
extraHeaders;
|
|
22
|
+
defaultStopSequences;
|
|
23
|
+
warnOnImageStrip;
|
|
24
|
+
constructor(config) {
|
|
25
|
+
if (!config.baseURL) {
|
|
26
|
+
throw new Error('OpenAI completions adapter requires baseURL');
|
|
27
|
+
}
|
|
28
|
+
this.name = config.providerName ?? 'openai-completions';
|
|
29
|
+
this.baseURL = config.baseURL.replace(/\/$/, ''); // Remove trailing slash
|
|
30
|
+
this.apiKey = config.apiKey ?? '';
|
|
31
|
+
this.defaultMaxTokens = config.defaultMaxTokens ?? 4096;
|
|
32
|
+
this.extraHeaders = config.extraHeaders ?? {};
|
|
33
|
+
this.defaultStopSequences = config.defaultStopSequences ?? ['\n\nHuman:', '\nHuman:'];
|
|
34
|
+
this.warnOnImageStrip = config.warnOnImageStrip ?? true;
|
|
35
|
+
}
|
|
36
|
+
supportsModel(_modelId) {
|
|
37
|
+
return true;
|
|
38
|
+
}
|
|
39
|
+
async complete(request, options) {
|
|
40
|
+
const completionsRequest = this.buildRequest(request);
|
|
41
|
+
try {
|
|
42
|
+
const response = await this.makeRequest(completionsRequest, options);
|
|
43
|
+
return this.parseResponse(response, request.model, completionsRequest);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
throw this.handleError(error, completionsRequest);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
async stream(request, callbacks, options) {
|
|
50
|
+
const completionsRequest = this.buildRequest(request);
|
|
51
|
+
completionsRequest.stream = true;
|
|
52
|
+
try {
|
|
53
|
+
const response = await fetch(`${this.baseURL}/completions`, {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
headers: this.getHeaders(),
|
|
56
|
+
body: JSON.stringify(completionsRequest),
|
|
57
|
+
signal: options?.signal,
|
|
58
|
+
});
|
|
59
|
+
if (!response.ok) {
|
|
60
|
+
const errorText = await response.text();
|
|
61
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
62
|
+
}
|
|
63
|
+
const reader = response.body?.getReader();
|
|
64
|
+
if (!reader) {
|
|
65
|
+
throw new Error('No response body');
|
|
66
|
+
}
|
|
67
|
+
const decoder = new TextDecoder();
|
|
68
|
+
let accumulated = '';
|
|
69
|
+
let finishReason = 'stop';
|
|
70
|
+
while (true) {
|
|
71
|
+
const { done, value } = await reader.read();
|
|
72
|
+
if (done)
|
|
73
|
+
break;
|
|
74
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
75
|
+
const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const data = line.slice(6);
|
|
78
|
+
if (data === '[DONE]')
|
|
79
|
+
continue;
|
|
80
|
+
try {
|
|
81
|
+
const parsed = JSON.parse(data);
|
|
82
|
+
const text = parsed.choices?.[0]?.text;
|
|
83
|
+
if (text) {
|
|
84
|
+
accumulated += text;
|
|
85
|
+
callbacks.onChunk(text);
|
|
86
|
+
}
|
|
87
|
+
if (parsed.choices?.[0]?.finish_reason) {
|
|
88
|
+
finishReason = parsed.choices[0].finish_reason;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// Ignore parse errors in stream
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return this.buildStreamedResponse(accumulated, finishReason, request.model, completionsRequest);
|
|
97
|
+
}
|
|
98
|
+
catch (error) {
|
|
99
|
+
throw this.handleError(error, completionsRequest);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
// ============================================================================
|
|
103
|
+
// Prompt Serialization
|
|
104
|
+
// ============================================================================
|
|
105
|
+
/**
|
|
106
|
+
* Serialize messages to Human:/Assistant: format for base models.
|
|
107
|
+
* Images are stripped from content.
|
|
108
|
+
*/
|
|
109
|
+
serializeToPrompt(messages) {
|
|
110
|
+
const parts = [];
|
|
111
|
+
let hasStrippedImages = false;
|
|
112
|
+
for (const msg of messages) {
|
|
113
|
+
const role = this.normalizeRole(msg.role);
|
|
114
|
+
const prefix = role === 'user' ? 'Human:' : 'Assistant:';
|
|
115
|
+
// Extract text content, strip images
|
|
116
|
+
const textContent = this.extractTextContent(msg.content);
|
|
117
|
+
if (textContent.hadImages) {
|
|
118
|
+
hasStrippedImages = true;
|
|
119
|
+
}
|
|
120
|
+
if (textContent.text) {
|
|
121
|
+
parts.push(`${prefix} ${textContent.text}`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
if (hasStrippedImages && this.warnOnImageStrip) {
|
|
125
|
+
console.warn('[OpenAICompletionsAdapter] Images were stripped from context (not supported in completions mode)');
|
|
126
|
+
}
|
|
127
|
+
// Add final Assistant: prefix to prompt completion
|
|
128
|
+
parts.push('Assistant:');
|
|
129
|
+
return parts.join('\n\n');
|
|
130
|
+
}
|
|
131
|
+
normalizeRole(role) {
|
|
132
|
+
if (role === 'user' || role === 'human' || role === 'Human') {
|
|
133
|
+
return 'user';
|
|
134
|
+
}
|
|
135
|
+
return 'assistant';
|
|
136
|
+
}
|
|
137
|
+
extractTextContent(content) {
|
|
138
|
+
if (typeof content === 'string') {
|
|
139
|
+
return { text: content, hadImages: false };
|
|
140
|
+
}
|
|
141
|
+
if (Array.isArray(content)) {
|
|
142
|
+
const textParts = [];
|
|
143
|
+
let hadImages = false;
|
|
144
|
+
for (const block of content) {
|
|
145
|
+
if (block.type === 'text') {
|
|
146
|
+
textParts.push(block.text);
|
|
147
|
+
}
|
|
148
|
+
else if (block.type === 'image' || block.type === 'image_url') {
|
|
149
|
+
hadImages = true;
|
|
150
|
+
}
|
|
151
|
+
// Skip tool_use, tool_result, thinking blocks for base models
|
|
152
|
+
}
|
|
153
|
+
return { text: textParts.join('\n'), hadImages };
|
|
154
|
+
}
|
|
155
|
+
return { text: '', hadImages: false };
|
|
156
|
+
}
|
|
157
|
+
// ============================================================================
|
|
158
|
+
// Private Methods
|
|
159
|
+
// ============================================================================
|
|
160
|
+
getHeaders() {
|
|
161
|
+
const headers = {
|
|
162
|
+
'Content-Type': 'application/json',
|
|
163
|
+
...this.extraHeaders,
|
|
164
|
+
};
|
|
165
|
+
if (this.apiKey) {
|
|
166
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
167
|
+
}
|
|
168
|
+
return headers;
|
|
169
|
+
}
|
|
170
|
+
buildRequest(request) {
|
|
171
|
+
const prompt = this.serializeToPrompt(request.messages);
|
|
172
|
+
const params = {
|
|
173
|
+
model: request.model,
|
|
174
|
+
prompt,
|
|
175
|
+
max_tokens: request.maxTokens || this.defaultMaxTokens,
|
|
176
|
+
};
|
|
177
|
+
if (request.temperature !== undefined) {
|
|
178
|
+
params.temperature = request.temperature;
|
|
179
|
+
}
|
|
180
|
+
// Combine default stop sequences with any provided ones
|
|
181
|
+
const stopSequences = [
|
|
182
|
+
...this.defaultStopSequences,
|
|
183
|
+
...(request.stopSequences || []),
|
|
184
|
+
];
|
|
185
|
+
if (stopSequences.length > 0) {
|
|
186
|
+
params.stop = stopSequences;
|
|
187
|
+
}
|
|
188
|
+
// Apply extra params (but not messages/tools which don't apply)
|
|
189
|
+
if (request.extra) {
|
|
190
|
+
const { messages, tools, ...rest } = request.extra;
|
|
191
|
+
Object.assign(params, rest);
|
|
192
|
+
}
|
|
193
|
+
return params;
|
|
194
|
+
}
|
|
195
|
+
async makeRequest(request, options) {
|
|
196
|
+
const response = await fetch(`${this.baseURL}/completions`, {
|
|
197
|
+
method: 'POST',
|
|
198
|
+
headers: this.getHeaders(),
|
|
199
|
+
body: JSON.stringify(request),
|
|
200
|
+
signal: options?.signal,
|
|
201
|
+
});
|
|
202
|
+
if (!response.ok) {
|
|
203
|
+
const errorText = await response.text();
|
|
204
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
205
|
+
}
|
|
206
|
+
return response.json();
|
|
207
|
+
}
|
|
208
|
+
parseResponse(response, requestedModel, rawRequest) {
|
|
209
|
+
const choice = response.choices[0];
|
|
210
|
+
const text = choice?.text ?? '';
|
|
211
|
+
return {
|
|
212
|
+
content: this.textToContent(text),
|
|
213
|
+
stopReason: this.mapFinishReason(choice?.finish_reason),
|
|
214
|
+
stopSequence: undefined,
|
|
215
|
+
usage: {
|
|
216
|
+
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
217
|
+
outputTokens: response.usage?.completion_tokens ?? 0,
|
|
218
|
+
},
|
|
219
|
+
model: response.model ?? requestedModel,
|
|
220
|
+
rawRequest,
|
|
221
|
+
raw: response,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
buildStreamedResponse(accumulated, finishReason, requestedModel, rawRequest) {
|
|
225
|
+
return {
|
|
226
|
+
content: this.textToContent(accumulated),
|
|
227
|
+
stopReason: this.mapFinishReason(finishReason),
|
|
228
|
+
stopSequence: undefined,
|
|
229
|
+
usage: {
|
|
230
|
+
inputTokens: 0, // Not available in streaming
|
|
231
|
+
outputTokens: 0,
|
|
232
|
+
},
|
|
233
|
+
model: requestedModel,
|
|
234
|
+
rawRequest,
|
|
235
|
+
raw: { text: accumulated, finish_reason: finishReason },
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
textToContent(text) {
|
|
239
|
+
// Trim leading whitespace (model often starts with space after "Assistant:")
|
|
240
|
+
const trimmed = text.replace(/^\s+/, '');
|
|
241
|
+
if (!trimmed)
|
|
242
|
+
return [];
|
|
243
|
+
return [{ type: 'text', text: trimmed }];
|
|
244
|
+
}
|
|
245
|
+
mapFinishReason(reason) {
|
|
246
|
+
switch (reason) {
|
|
247
|
+
case 'stop':
|
|
248
|
+
return 'end_turn';
|
|
249
|
+
case 'length':
|
|
250
|
+
return 'max_tokens';
|
|
251
|
+
default:
|
|
252
|
+
return 'end_turn';
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
handleError(error, rawRequest) {
|
|
256
|
+
if (error instanceof Error) {
|
|
257
|
+
const message = error.message;
|
|
258
|
+
if (message.includes('429') || message.includes('rate')) {
|
|
259
|
+
return rateLimitError(message, undefined, error, rawRequest);
|
|
260
|
+
}
|
|
261
|
+
if (message.includes('401') || message.includes('auth') || message.includes('Unauthorized')) {
|
|
262
|
+
return authError(message, error, rawRequest);
|
|
263
|
+
}
|
|
264
|
+
if (message.includes('context') || message.includes('too long') || message.includes('maximum context')) {
|
|
265
|
+
return contextLengthError(message, error, rawRequest);
|
|
266
|
+
}
|
|
267
|
+
if (message.includes('500') || message.includes('502') || message.includes('503')) {
|
|
268
|
+
return serverError(message, undefined, error, rawRequest);
|
|
269
|
+
}
|
|
270
|
+
if (error.name === 'AbortError') {
|
|
271
|
+
return abortError(undefined, rawRequest);
|
|
272
|
+
}
|
|
273
|
+
if (message.includes('network') || message.includes('fetch') || message.includes('ECONNREFUSED')) {
|
|
274
|
+
return networkError(message, error, rawRequest);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return new MembraneError({
|
|
278
|
+
type: 'unknown',
|
|
279
|
+
message: error instanceof Error ? error.message : String(error),
|
|
280
|
+
retryable: false,
|
|
281
|
+
rawError: error,
|
|
282
|
+
rawRequest,
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
//# sourceMappingURL=openai-completions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openai-completions.js","sourceRoot":"","sources":["../../src/providers/openai-completions.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/providers/index.ts
CHANGED
|
@@ -0,0 +1,429 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-Compatible Completions adapter for base models
|
|
3
|
+
*
|
|
4
|
+
* For true base/completion models that use the `/v1/completions` endpoint:
|
|
5
|
+
* - No chat formatting built-in
|
|
6
|
+
* - Single text prompt input
|
|
7
|
+
* - Raw completion output
|
|
8
|
+
* - No image support
|
|
9
|
+
*
|
|
10
|
+
* Serializes conversations to Human:/Assistant: format.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import type {
|
|
14
|
+
ProviderAdapter,
|
|
15
|
+
ProviderRequest,
|
|
16
|
+
ProviderRequestOptions,
|
|
17
|
+
ProviderResponse,
|
|
18
|
+
StreamCallbacks,
|
|
19
|
+
ContentBlock,
|
|
20
|
+
} from '../types/index.js';
|
|
21
|
+
import {
|
|
22
|
+
MembraneError,
|
|
23
|
+
rateLimitError,
|
|
24
|
+
contextLengthError,
|
|
25
|
+
authError,
|
|
26
|
+
serverError,
|
|
27
|
+
abortError,
|
|
28
|
+
networkError,
|
|
29
|
+
} from '../types/index.js';
|
|
30
|
+
|
|
31
|
+
// ============================================================================
|
|
32
|
+
// Types
|
|
33
|
+
// ============================================================================
|
|
34
|
+
|
|
35
|
+
interface CompletionsRequest {
|
|
36
|
+
model: string;
|
|
37
|
+
prompt: string;
|
|
38
|
+
max_tokens?: number;
|
|
39
|
+
temperature?: number;
|
|
40
|
+
stop?: string[];
|
|
41
|
+
stream?: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
interface CompletionsResponse {
|
|
45
|
+
id: string;
|
|
46
|
+
model: string;
|
|
47
|
+
choices: {
|
|
48
|
+
index: number;
|
|
49
|
+
text: string;
|
|
50
|
+
finish_reason: string;
|
|
51
|
+
}[];
|
|
52
|
+
usage?: {
|
|
53
|
+
prompt_tokens: number;
|
|
54
|
+
completion_tokens: number;
|
|
55
|
+
total_tokens: number;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// ============================================================================
|
|
60
|
+
// Adapter Configuration
|
|
61
|
+
// ============================================================================
|
|
62
|
+
|
|
63
|
+
export interface OpenAICompletionsAdapterConfig {
|
|
64
|
+
/** Base URL for the API (required, e.g., 'http://localhost:8000/v1') */
|
|
65
|
+
baseURL: string;
|
|
66
|
+
|
|
67
|
+
/** API key (optional for local servers) */
|
|
68
|
+
apiKey?: string;
|
|
69
|
+
|
|
70
|
+
/** Provider name for logging/identification (default: 'openai-completions') */
|
|
71
|
+
providerName?: string;
|
|
72
|
+
|
|
73
|
+
/** Default max tokens */
|
|
74
|
+
defaultMaxTokens?: number;
|
|
75
|
+
|
|
76
|
+
/** Additional headers to include with requests */
|
|
77
|
+
extraHeaders?: Record<string, string>;
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Stop sequences to use (default: ['\n\nHuman:', '\nHuman:'])
|
|
81
|
+
* These prevent the model from generating user turns.
|
|
82
|
+
*/
|
|
83
|
+
defaultStopSequences?: string[];
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Whether to warn when images are stripped from context (default: true)
|
|
87
|
+
*/
|
|
88
|
+
warnOnImageStrip?: boolean;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// OpenAI Completions Adapter
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
export class OpenAICompletionsAdapter implements ProviderAdapter {
|
|
96
|
+
readonly name: string;
|
|
97
|
+
private baseURL: string;
|
|
98
|
+
private apiKey: string;
|
|
99
|
+
private defaultMaxTokens: number;
|
|
100
|
+
private extraHeaders: Record<string, string>;
|
|
101
|
+
private defaultStopSequences: string[];
|
|
102
|
+
private warnOnImageStrip: boolean;
|
|
103
|
+
|
|
104
|
+
constructor(config: OpenAICompletionsAdapterConfig) {
|
|
105
|
+
if (!config.baseURL) {
|
|
106
|
+
throw new Error('OpenAI completions adapter requires baseURL');
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
this.name = config.providerName ?? 'openai-completions';
|
|
110
|
+
this.baseURL = config.baseURL.replace(/\/$/, ''); // Remove trailing slash
|
|
111
|
+
this.apiKey = config.apiKey ?? '';
|
|
112
|
+
this.defaultMaxTokens = config.defaultMaxTokens ?? 4096;
|
|
113
|
+
this.extraHeaders = config.extraHeaders ?? {};
|
|
114
|
+
this.defaultStopSequences = config.defaultStopSequences ?? ['\n\nHuman:', '\nHuman:'];
|
|
115
|
+
this.warnOnImageStrip = config.warnOnImageStrip ?? true;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
supportsModel(_modelId: string): boolean {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async complete(
|
|
123
|
+
request: ProviderRequest,
|
|
124
|
+
options?: ProviderRequestOptions
|
|
125
|
+
): Promise<ProviderResponse> {
|
|
126
|
+
const completionsRequest = this.buildRequest(request);
|
|
127
|
+
|
|
128
|
+
try {
|
|
129
|
+
const response = await this.makeRequest(completionsRequest, options);
|
|
130
|
+
return this.parseResponse(response, request.model, completionsRequest);
|
|
131
|
+
} catch (error) {
|
|
132
|
+
throw this.handleError(error, completionsRequest);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
async stream(
|
|
137
|
+
request: ProviderRequest,
|
|
138
|
+
callbacks: StreamCallbacks,
|
|
139
|
+
options?: ProviderRequestOptions
|
|
140
|
+
): Promise<ProviderResponse> {
|
|
141
|
+
const completionsRequest = this.buildRequest(request);
|
|
142
|
+
completionsRequest.stream = true;
|
|
143
|
+
|
|
144
|
+
try {
|
|
145
|
+
const response = await fetch(`${this.baseURL}/completions`, {
|
|
146
|
+
method: 'POST',
|
|
147
|
+
headers: this.getHeaders(),
|
|
148
|
+
body: JSON.stringify(completionsRequest),
|
|
149
|
+
signal: options?.signal,
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
const errorText = await response.text();
|
|
154
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const reader = response.body?.getReader();
|
|
158
|
+
if (!reader) {
|
|
159
|
+
throw new Error('No response body');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
const decoder = new TextDecoder();
|
|
163
|
+
let accumulated = '';
|
|
164
|
+
let finishReason = 'stop';
|
|
165
|
+
|
|
166
|
+
while (true) {
|
|
167
|
+
const { done, value } = await reader.read();
|
|
168
|
+
if (done) break;
|
|
169
|
+
|
|
170
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
171
|
+
const lines = chunk.split('\n').filter(line => line.startsWith('data: '));
|
|
172
|
+
|
|
173
|
+
for (const line of lines) {
|
|
174
|
+
const data = line.slice(6);
|
|
175
|
+
if (data === '[DONE]') continue;
|
|
176
|
+
|
|
177
|
+
try {
|
|
178
|
+
const parsed = JSON.parse(data);
|
|
179
|
+
const text = parsed.choices?.[0]?.text;
|
|
180
|
+
|
|
181
|
+
if (text) {
|
|
182
|
+
accumulated += text;
|
|
183
|
+
callbacks.onChunk(text);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
if (parsed.choices?.[0]?.finish_reason) {
|
|
187
|
+
finishReason = parsed.choices[0].finish_reason;
|
|
188
|
+
}
|
|
189
|
+
} catch {
|
|
190
|
+
// Ignore parse errors in stream
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
return this.buildStreamedResponse(accumulated, finishReason, request.model, completionsRequest);
|
|
196
|
+
|
|
197
|
+
} catch (error) {
|
|
198
|
+
throw this.handleError(error, completionsRequest);
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// ============================================================================
|
|
203
|
+
// Prompt Serialization
|
|
204
|
+
// ============================================================================
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Serialize messages to Human:/Assistant: format for base models.
|
|
208
|
+
* Images are stripped from content.
|
|
209
|
+
*/
|
|
210
|
+
serializeToPrompt(messages: any[]): string {
|
|
211
|
+
const parts: string[] = [];
|
|
212
|
+
let hasStrippedImages = false;
|
|
213
|
+
|
|
214
|
+
for (const msg of messages) {
|
|
215
|
+
const role = this.normalizeRole(msg.role);
|
|
216
|
+
const prefix = role === 'user' ? 'Human:' : 'Assistant:';
|
|
217
|
+
|
|
218
|
+
// Extract text content, strip images
|
|
219
|
+
const textContent = this.extractTextContent(msg.content);
|
|
220
|
+
if (textContent.hadImages) {
|
|
221
|
+
hasStrippedImages = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (textContent.text) {
|
|
225
|
+
parts.push(`${prefix} ${textContent.text}`);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (hasStrippedImages && this.warnOnImageStrip) {
|
|
230
|
+
console.warn('[OpenAICompletionsAdapter] Images were stripped from context (not supported in completions mode)');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Add final Assistant: prefix to prompt completion
|
|
234
|
+
parts.push('Assistant:');
|
|
235
|
+
|
|
236
|
+
return parts.join('\n\n');
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private normalizeRole(role: string): 'user' | 'assistant' {
|
|
240
|
+
if (role === 'user' || role === 'human' || role === 'Human') {
|
|
241
|
+
return 'user';
|
|
242
|
+
}
|
|
243
|
+
return 'assistant';
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private extractTextContent(content: any): { text: string; hadImages: boolean } {
|
|
247
|
+
if (typeof content === 'string') {
|
|
248
|
+
return { text: content, hadImages: false };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (Array.isArray(content)) {
|
|
252
|
+
const textParts: string[] = [];
|
|
253
|
+
let hadImages = false;
|
|
254
|
+
|
|
255
|
+
for (const block of content) {
|
|
256
|
+
if (block.type === 'text') {
|
|
257
|
+
textParts.push(block.text);
|
|
258
|
+
} else if (block.type === 'image' || block.type === 'image_url') {
|
|
259
|
+
hadImages = true;
|
|
260
|
+
}
|
|
261
|
+
// Skip tool_use, tool_result, thinking blocks for base models
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return { text: textParts.join('\n'), hadImages };
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
return { text: '', hadImages: false };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// ============================================================================
|
|
271
|
+
// Private Methods
|
|
272
|
+
// ============================================================================
|
|
273
|
+
|
|
274
|
+
private getHeaders(): Record<string, string> {
|
|
275
|
+
const headers: Record<string, string> = {
|
|
276
|
+
'Content-Type': 'application/json',
|
|
277
|
+
...this.extraHeaders,
|
|
278
|
+
};
|
|
279
|
+
|
|
280
|
+
if (this.apiKey) {
|
|
281
|
+
headers['Authorization'] = `Bearer ${this.apiKey}`;
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
return headers;
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
private buildRequest(request: ProviderRequest): CompletionsRequest {
|
|
288
|
+
const prompt = this.serializeToPrompt(request.messages as any[]);
|
|
289
|
+
|
|
290
|
+
const params: CompletionsRequest = {
|
|
291
|
+
model: request.model,
|
|
292
|
+
prompt,
|
|
293
|
+
max_tokens: request.maxTokens || this.defaultMaxTokens,
|
|
294
|
+
};
|
|
295
|
+
|
|
296
|
+
if (request.temperature !== undefined) {
|
|
297
|
+
params.temperature = request.temperature;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
// Combine default stop sequences with any provided ones
|
|
301
|
+
const stopSequences = [
|
|
302
|
+
...this.defaultStopSequences,
|
|
303
|
+
...(request.stopSequences || []),
|
|
304
|
+
];
|
|
305
|
+
if (stopSequences.length > 0) {
|
|
306
|
+
params.stop = stopSequences;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// Apply extra params (but not messages/tools which don't apply)
|
|
310
|
+
if (request.extra) {
|
|
311
|
+
const { messages, tools, ...rest } = request.extra as any;
|
|
312
|
+
Object.assign(params, rest);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
return params;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
private async makeRequest(request: CompletionsRequest, options?: ProviderRequestOptions): Promise<CompletionsResponse> {
|
|
319
|
+
const response = await fetch(`${this.baseURL}/completions`, {
|
|
320
|
+
method: 'POST',
|
|
321
|
+
headers: this.getHeaders(),
|
|
322
|
+
body: JSON.stringify(request),
|
|
323
|
+
signal: options?.signal,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
if (!response.ok) {
|
|
327
|
+
const errorText = await response.text();
|
|
328
|
+
throw new Error(`API error: ${response.status} ${errorText}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
return response.json() as Promise<CompletionsResponse>;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
private parseResponse(response: CompletionsResponse, requestedModel: string, rawRequest: unknown): ProviderResponse {
|
|
335
|
+
const choice = response.choices[0];
|
|
336
|
+
const text = choice?.text ?? '';
|
|
337
|
+
|
|
338
|
+
return {
|
|
339
|
+
content: this.textToContent(text),
|
|
340
|
+
stopReason: this.mapFinishReason(choice?.finish_reason),
|
|
341
|
+
stopSequence: undefined,
|
|
342
|
+
usage: {
|
|
343
|
+
inputTokens: response.usage?.prompt_tokens ?? 0,
|
|
344
|
+
outputTokens: response.usage?.completion_tokens ?? 0,
|
|
345
|
+
},
|
|
346
|
+
model: response.model ?? requestedModel,
|
|
347
|
+
rawRequest,
|
|
348
|
+
raw: response,
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
private buildStreamedResponse(
|
|
353
|
+
accumulated: string,
|
|
354
|
+
finishReason: string,
|
|
355
|
+
requestedModel: string,
|
|
356
|
+
rawRequest?: unknown
|
|
357
|
+
): ProviderResponse {
|
|
358
|
+
return {
|
|
359
|
+
content: this.textToContent(accumulated),
|
|
360
|
+
stopReason: this.mapFinishReason(finishReason),
|
|
361
|
+
stopSequence: undefined,
|
|
362
|
+
usage: {
|
|
363
|
+
inputTokens: 0, // Not available in streaming
|
|
364
|
+
outputTokens: 0,
|
|
365
|
+
},
|
|
366
|
+
model: requestedModel,
|
|
367
|
+
rawRequest,
|
|
368
|
+
raw: { text: accumulated, finish_reason: finishReason },
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
private textToContent(text: string): ContentBlock[] {
|
|
373
|
+
// Trim leading whitespace (model often starts with space after "Assistant:")
|
|
374
|
+
const trimmed = text.replace(/^\s+/, '');
|
|
375
|
+
|
|
376
|
+
if (!trimmed) return [];
|
|
377
|
+
|
|
378
|
+
return [{ type: 'text', text: trimmed }];
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private mapFinishReason(reason: string | undefined): string {
|
|
382
|
+
switch (reason) {
|
|
383
|
+
case 'stop':
|
|
384
|
+
return 'end_turn';
|
|
385
|
+
case 'length':
|
|
386
|
+
return 'max_tokens';
|
|
387
|
+
default:
|
|
388
|
+
return 'end_turn';
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
private handleError(error: unknown, rawRequest?: unknown): MembraneError {
|
|
393
|
+
if (error instanceof Error) {
|
|
394
|
+
const message = error.message;
|
|
395
|
+
|
|
396
|
+
if (message.includes('429') || message.includes('rate')) {
|
|
397
|
+
return rateLimitError(message, undefined, error, rawRequest);
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (message.includes('401') || message.includes('auth') || message.includes('Unauthorized')) {
|
|
401
|
+
return authError(message, error, rawRequest);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
if (message.includes('context') || message.includes('too long') || message.includes('maximum context')) {
|
|
405
|
+
return contextLengthError(message, error, rawRequest);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (message.includes('500') || message.includes('502') || message.includes('503')) {
|
|
409
|
+
return serverError(message, undefined, error, rawRequest);
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
if (error.name === 'AbortError') {
|
|
413
|
+
return abortError(undefined, rawRequest);
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
if (message.includes('network') || message.includes('fetch') || message.includes('ECONNREFUSED')) {
|
|
417
|
+
return networkError(message, error, rawRequest);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
return new MembraneError({
|
|
422
|
+
type: 'unknown',
|
|
423
|
+
message: error instanceof Error ? error.message : String(error),
|
|
424
|
+
retryable: false,
|
|
425
|
+
rawError: error,
|
|
426
|
+
rawRequest,
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|