echospace 0.0.1 → 0.1.0-alpha.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,384 @@
1
+ // src/core/providers/anthropic.ts
2
+ function toAnthropicContent(msg) {
3
+ const blocks = [];
4
+ for (const part of msg.parts) {
5
+ switch (part.type) {
6
+ case "text":
7
+ blocks.push({ type: "text", text: part.text });
8
+ break;
9
+ case "thinking":
10
+ blocks.push({ type: "thinking", thinking: part.text });
11
+ break;
12
+ case "tool_call":
13
+ blocks.push({
14
+ type: "tool_use",
15
+ id: part.id,
16
+ name: part.name,
17
+ input: part.input
18
+ });
19
+ break;
20
+ case "tool_result":
21
+ blocks.push({
22
+ type: "tool_result",
23
+ tool_use_id: part.id,
24
+ content: typeof part.output === "string" ? part.output : JSON.stringify(part.output),
25
+ is_error: part.is_error
26
+ });
27
+ break;
28
+ case "image":
29
+ if (part.base64) {
30
+ blocks.push({
31
+ type: "image",
32
+ source: {
33
+ type: "base64",
34
+ media_type: part.media_type ?? "image/png",
35
+ data: part.base64
36
+ }
37
+ });
38
+ }
39
+ break;
40
+ }
41
+ }
42
+ return blocks.length > 0 ? blocks : [{ type: "text", text: "" }];
43
+ }
44
+ var anthropicAdapter = {
45
+ type: "anthropic",
46
+ buildRequest(messages, settings, config) {
47
+ const baseUrl = config.base_url ?? "https://api.anthropic.com";
48
+ const url = `${baseUrl.replace(/\/$/, "")}/v1/messages`;
49
+ const systemMessages = messages.filter((m) => m.role === "system");
50
+ const nonSystemMessages = messages.filter((m) => m.role !== "system");
51
+ const system = systemMessages.flatMap((m) => m.parts.filter((p) => p.type === "text")).map((p) => p.text).join("\n\n");
52
+ const body = {
53
+ model: settings.model ?? config.models[0],
54
+ messages: nonSystemMessages.map((msg) => ({
55
+ role: msg.role === "tool" ? "user" : msg.role,
56
+ content: toAnthropicContent(msg)
57
+ })),
58
+ max_tokens: settings.max_tokens ?? 4096,
59
+ stream: true
60
+ };
61
+ if (system) body.system = system;
62
+ if (settings.temperature != null) body.temperature = settings.temperature;
63
+ if (settings.top_p != null) body.top_p = settings.top_p;
64
+ if (settings.tools && settings.tools.length > 0) {
65
+ body.tools = settings.tools.map((t) => ({
66
+ name: t.name,
67
+ description: t.description,
68
+ input_schema: t.parameters
69
+ }));
70
+ }
71
+ const headers = {
72
+ "Content-Type": "application/json",
73
+ "anthropic-version": "2023-06-01"
74
+ };
75
+ if (config.api_key) {
76
+ headers["x-api-key"] = config.api_key;
77
+ }
78
+ return { url, headers, body };
79
+ },
80
+ parseChunk(chunk) {
81
+ try {
82
+ const event = JSON.parse(chunk);
83
+ const parts = [];
84
+ switch (event.type) {
85
+ case "content_block_delta": {
86
+ const delta = event.delta;
87
+ if (delta?.type === "text_delta" && delta.text) {
88
+ parts.push({ type: "text", text: delta.text });
89
+ }
90
+ if (delta?.type === "thinking_delta" && delta.thinking) {
91
+ parts.push({ type: "thinking", text: delta.thinking });
92
+ }
93
+ if (delta?.type === "input_json_delta" && delta.partial_json) {
94
+ parts.push({
95
+ type: "tool_call",
96
+ id: "",
97
+ name: "",
98
+ input: delta.partial_json
99
+ });
100
+ }
101
+ break;
102
+ }
103
+ case "content_block_start": {
104
+ const block = event.content_block;
105
+ if (block?.type === "tool_use") {
106
+ parts.push({
107
+ type: "tool_call",
108
+ id: block.id ?? "",
109
+ name: block.name ?? "",
110
+ input: ""
111
+ });
112
+ }
113
+ break;
114
+ }
115
+ }
116
+ return parts;
117
+ } catch {
118
+ return [];
119
+ }
120
+ },
121
+ isDone(chunk) {
122
+ try {
123
+ const event = JSON.parse(chunk);
124
+ return event.type === "message_stop";
125
+ } catch {
126
+ return false;
127
+ }
128
+ }
129
+ };
130
+
131
+ // src/core/providers/google.ts
132
+ function toGeminiContent(msg) {
133
+ const parts = [];
134
+ for (const part of msg.parts) {
135
+ switch (part.type) {
136
+ case "text":
137
+ case "thinking":
138
+ parts.push({ text: part.text });
139
+ break;
140
+ case "tool_call":
141
+ parts.push({
142
+ functionCall: {
143
+ name: part.name,
144
+ args: typeof part.input === "string" ? JSON.parse(part.input) : part.input
145
+ }
146
+ });
147
+ break;
148
+ case "tool_result":
149
+ parts.push({
150
+ functionResponse: {
151
+ name: part.id,
152
+ response: { result: part.output }
153
+ }
154
+ });
155
+ break;
156
+ case "image":
157
+ if (part.base64) {
158
+ parts.push({
159
+ inlineData: {
160
+ mimeType: part.media_type ?? "image/png",
161
+ data: part.base64
162
+ }
163
+ });
164
+ }
165
+ break;
166
+ }
167
+ }
168
+ const role = msg.role === "assistant" ? "model" : "user";
169
+ return { role, parts };
170
+ }
171
+ var googleAdapter = {
172
+ type: "google",
173
+ buildRequest(messages, settings, config) {
174
+ const model = settings.model ?? config.models[0];
175
+ const baseUrl = config.base_url ?? "https://generativelanguage.googleapis.com/v1beta";
176
+ const url = `${baseUrl}/models/${model}:streamGenerateContent?alt=sse&key=${config.api_key ?? ""}`;
177
+ const systemMessages = messages.filter((m) => m.role === "system");
178
+ const nonSystemMessages = messages.filter((m) => m.role !== "system");
179
+ const systemInstruction = systemMessages.length > 0 ? {
180
+ parts: systemMessages.flatMap(
181
+ (m) => m.parts.filter((p) => p.type === "text").map((p) => ({ text: p.text }))
182
+ )
183
+ } : void 0;
184
+ const body = {
185
+ contents: nonSystemMessages.map(toGeminiContent),
186
+ generationConfig: {
187
+ ...settings.temperature != null && {
188
+ temperature: settings.temperature
189
+ },
190
+ ...settings.max_tokens != null && {
191
+ maxOutputTokens: settings.max_tokens
192
+ },
193
+ ...settings.top_p != null && { topP: settings.top_p }
194
+ }
195
+ };
196
+ if (systemInstruction) body.systemInstruction = systemInstruction;
197
+ if (settings.tools && settings.tools.length > 0) {
198
+ body.tools = [
199
+ {
200
+ functionDeclarations: settings.tools.map((t) => ({
201
+ name: t.name,
202
+ description: t.description,
203
+ parameters: t.parameters
204
+ }))
205
+ }
206
+ ];
207
+ }
208
+ const headers = {
209
+ "Content-Type": "application/json"
210
+ };
211
+ return { url, headers, body };
212
+ },
213
+ parseChunk(chunk) {
214
+ try {
215
+ const data = JSON.parse(chunk);
216
+ const candidate = data?.candidates?.[0];
217
+ if (!candidate?.content?.parts) return [];
218
+ const parts = [];
219
+ for (const part of candidate.content.parts) {
220
+ if (part.text) {
221
+ parts.push({ type: "text", text: part.text });
222
+ }
223
+ if (part.functionCall) {
224
+ parts.push({
225
+ type: "tool_call",
226
+ id: part.functionCall.name ?? "",
227
+ name: part.functionCall.name ?? "",
228
+ input: part.functionCall.args ?? {}
229
+ });
230
+ }
231
+ }
232
+ return parts;
233
+ } catch {
234
+ return [];
235
+ }
236
+ },
237
+ isDone(chunk) {
238
+ try {
239
+ const data = JSON.parse(chunk);
240
+ const candidate = data?.candidates?.[0];
241
+ return candidate?.finishReason === "STOP";
242
+ } catch {
243
+ return false;
244
+ }
245
+ }
246
+ };
247
+
248
+ // src/core/providers/openai.ts
249
+ function toOpenAIMessage(msg) {
250
+ const result = { role: msg.role };
251
+ const textParts = msg.parts.filter((p) => p.type === "text");
252
+ const toolCalls = msg.parts.filter((p) => p.type === "tool_call");
253
+ const toolResults = msg.parts.filter((p) => p.type === "tool_result");
254
+ const thinkingParts = msg.parts.filter((p) => p.type === "thinking");
255
+ if (msg.role === "tool" && toolResults.length > 0) {
256
+ const tr = toolResults[0];
257
+ result.content = typeof tr.output === "string" ? tr.output : JSON.stringify(tr.output);
258
+ result.tool_call_id = tr.id;
259
+ return result;
260
+ }
261
+ if (msg.role === "assistant") {
262
+ result.content = textParts.map((p) => p.text).join("") || null;
263
+ if (thinkingParts.length > 0) {
264
+ result.reasoning_content = thinkingParts.map((p) => p.text).join("");
265
+ }
266
+ if (toolCalls.length > 0) {
267
+ result.tool_calls = toolCalls.map((tc) => ({
268
+ id: tc.id,
269
+ type: "function",
270
+ function: {
271
+ name: tc.name,
272
+ arguments: typeof tc.input === "string" ? tc.input : JSON.stringify(tc.input)
273
+ }
274
+ }));
275
+ }
276
+ return result;
277
+ }
278
+ result.content = textParts.map((p) => p.text).join("");
279
+ return result;
280
+ }
281
+ var openaiAdapter = {
282
+ type: "openai",
283
+ buildRequest(messages, settings, config) {
284
+ const baseUrl = config.base_url ?? "https://api.openai.com/v1";
285
+ const url = `${baseUrl.replace(/\/$/, "")}/chat/completions`;
286
+ const body = {
287
+ model: settings.model ?? config.models[0],
288
+ messages: messages.map(toOpenAIMessage),
289
+ stream: true
290
+ };
291
+ if (settings.temperature != null) body.temperature = settings.temperature;
292
+ if (settings.max_tokens != null) body.max_tokens = settings.max_tokens;
293
+ if (settings.top_p != null) body.top_p = settings.top_p;
294
+ if (settings.response_format && settings.response_format !== "text") {
295
+ if (settings.response_format === "json_schema" && settings.json_schema) {
296
+ body.response_format = {
297
+ type: "json_schema",
298
+ json_schema: settings.json_schema
299
+ };
300
+ } else {
301
+ body.response_format = { type: settings.response_format };
302
+ }
303
+ }
304
+ if (settings.tools && settings.tools.length > 0) {
305
+ body.tools = settings.tools.map((t) => ({
306
+ type: "function",
307
+ function: {
308
+ name: t.name,
309
+ description: t.description,
310
+ parameters: t.parameters,
311
+ ...t.strict != null && { strict: t.strict }
312
+ }
313
+ }));
314
+ }
315
+ const headers = {
316
+ "Content-Type": "application/json"
317
+ };
318
+ if (config.api_key) {
319
+ headers["Authorization"] = `Bearer ${config.api_key}`;
320
+ }
321
+ return { url, headers, body };
322
+ },
323
+ parseChunk(chunk) {
324
+ if (chunk === "[DONE]") return [];
325
+ try {
326
+ const data = JSON.parse(chunk);
327
+ const delta = data?.choices?.[0]?.delta;
328
+ if (!delta) return [];
329
+ const parts = [];
330
+ if (delta.content) {
331
+ parts.push({ type: "text", text: delta.content });
332
+ }
333
+ if (delta.reasoning_content) {
334
+ parts.push({ type: "thinking", text: delta.reasoning_content });
335
+ }
336
+ if (delta.tool_calls) {
337
+ for (const tc of delta.tool_calls) {
338
+ if (tc.function) {
339
+ parts.push({
340
+ type: "tool_call",
341
+ id: tc.id ?? "",
342
+ name: tc.function.name ?? "",
343
+ input: tc.function.arguments ?? ""
344
+ });
345
+ }
346
+ }
347
+ }
348
+ return parts;
349
+ } catch {
350
+ return [];
351
+ }
352
+ },
353
+ isDone(chunk) {
354
+ return chunk === "[DONE]";
355
+ }
356
+ };
357
+
358
+ // src/core/providers/registry.ts
359
+ var DefaultProviderRegistry = class {
360
+ adapters = /* @__PURE__ */ new Map();
361
+ get(type) {
362
+ const adapter = this.adapters.get(type);
363
+ if (!adapter) {
364
+ throw new Error(`Unknown provider type: ${type}`);
365
+ }
366
+ return adapter;
367
+ }
368
+ register(adapter) {
369
+ this.adapters.set(adapter.type, adapter);
370
+ }
371
+ };
372
+ function createProviderRegistry() {
373
+ const registry = new DefaultProviderRegistry();
374
+ registry.register(openaiAdapter);
375
+ registry.register(anthropicAdapter);
376
+ registry.register(googleAdapter);
377
+ return registry;
378
+ }
379
+ export {
380
+ anthropicAdapter,
381
+ createProviderRegistry,
382
+ googleAdapter,
383
+ openaiAdapter
384
+ };
@@ -0,0 +1,15 @@
1
+ import { a as EchoMessage } from '../types-CYpEmXbp.js';
2
+
3
+ type DetectedFormat = "openai" | "anthropic" | "google" | "vercel" | "echo" | "raw" | "unknown";
4
+ /**
5
+ * Detect the format of pasted conversation text.
6
+ */
7
+ declare function detectFormat(input: string): DetectedFormat;
8
+
9
+ /**
10
+ * Smart-parse any pasted conversation text into EchoMessages.
11
+ * Auto-detects the format and converts accordingly.
12
+ */
13
+ declare function smartParse(input: string): EchoMessage[];
14
+
15
+ export { type DetectedFormat, detectFormat, smartParse };