@moraya/core 0.2.0 → 0.4.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.
Files changed (54) hide show
  1. package/README.md +215 -41
  2. package/dist/ai/drivers/claude.d.ts +6 -0
  3. package/dist/ai/drivers/claude.js +229 -0
  4. package/dist/ai/drivers/claude.js.map +1 -0
  5. package/dist/ai/drivers/gemini.d.ts +6 -0
  6. package/dist/ai/drivers/gemini.js +212 -0
  7. package/dist/ai/drivers/gemini.js.map +1 -0
  8. package/dist/ai/drivers/index.d.ts +14 -0
  9. package/dist/ai/drivers/index.js +617 -0
  10. package/dist/ai/drivers/index.js.map +1 -0
  11. package/dist/ai/drivers/ollama.d.ts +8 -0
  12. package/dist/ai/drivers/ollama.js +158 -0
  13. package/dist/ai/drivers/ollama.js.map +1 -0
  14. package/dist/ai/drivers/openai.d.ts +7 -0
  15. package/dist/ai/drivers/openai.js +225 -0
  16. package/dist/ai/drivers/openai.js.map +1 -0
  17. package/dist/ai/drivers/tool-bridge.d.ts +37 -0
  18. package/dist/ai/drivers/tool-bridge.js +138 -0
  19. package/dist/ai/drivers/tool-bridge.js.map +1 -0
  20. package/dist/ai/drivers/types.d.ts +2 -0
  21. package/dist/ai/drivers/types.js +1 -0
  22. package/dist/ai/drivers/types.js.map +1 -0
  23. package/dist/ai/drivers/util.d.ts +13 -0
  24. package/dist/ai/drivers/util.js +40 -0
  25. package/dist/ai/drivers/util.js.map +1 -0
  26. package/dist/ai/image.d.ts +37 -0
  27. package/dist/ai/image.js +36 -0
  28. package/dist/ai/image.js.map +1 -0
  29. package/dist/ai/index.d.ts +37 -0
  30. package/dist/ai/index.js +826 -0
  31. package/dist/ai/index.js.map +1 -0
  32. package/dist/ai/types.d.ts +92 -0
  33. package/dist/ai/types.js +1 -0
  34. package/dist/ai/types.js.map +1 -0
  35. package/dist/ai/voice.d.ts +42 -0
  36. package/dist/ai/voice.js +34 -0
  37. package/dist/ai/voice.js.map +1 -0
  38. package/dist/chat-markdown/index.d.ts +82 -0
  39. package/dist/chat-markdown/index.js +165 -0
  40. package/dist/chat-markdown/index.js.map +1 -0
  41. package/dist/i18n/locales/ar.json +806 -732
  42. package/dist/i18n/locales/de.json +912 -838
  43. package/dist/i18n/locales/en.json +34 -5
  44. package/dist/i18n/locales/es.json +952 -876
  45. package/dist/i18n/locales/fr.json +1784 -1708
  46. package/dist/i18n/locales/hi.json +1808 -1734
  47. package/dist/i18n/locales/ja.json +839 -765
  48. package/dist/i18n/locales/ko.json +1783 -1709
  49. package/dist/i18n/locales/pt.json +894 -820
  50. package/dist/i18n/locales/ru.json +812 -738
  51. package/dist/i18n/locales/zh-CN.json +34 -5
  52. package/dist/i18n/locales/zh-Hant.json +1039 -965
  53. package/dist/types-CwM77g7u.d.ts +88 -0
  54. package/package.json +26 -2
@@ -0,0 +1,138 @@
1
+ // src/ai/drivers/tool-bridge.ts
2
+ var GEMINI_UNSUPPORTED_KEYS = /* @__PURE__ */ new Set([
3
+ "additionalProperties",
4
+ "$schema",
5
+ "$id",
6
+ "$ref",
7
+ "$defs",
8
+ "definitions",
9
+ "patternProperties",
10
+ "unevaluatedProperties",
11
+ "dependentRequired",
12
+ "dependentSchemas",
13
+ "const"
14
+ ]);
15
+ function sanitizeGeminiSchema(schema) {
16
+ if (Array.isArray(schema)) return schema.map(sanitizeGeminiSchema);
17
+ if (schema && typeof schema === "object") {
18
+ const out = {};
19
+ for (const [key, value] of Object.entries(schema)) {
20
+ if (GEMINI_UNSUPPORTED_KEYS.has(key)) continue;
21
+ out[key] = sanitizeGeminiSchema(value);
22
+ }
23
+ return out;
24
+ }
25
+ return schema;
26
+ }
27
+ function formatToolsForProvider(provider, tools) {
28
+ if (tools.length === 0) return {};
29
+ switch (provider) {
30
+ case "claude":
31
+ return {
32
+ tools: tools.map((t) => ({ name: t.name, description: t.description, input_schema: t.input_schema }))
33
+ };
34
+ case "gemini":
35
+ return {
36
+ tools: [{
37
+ functionDeclarations: tools.map((t) => ({
38
+ name: t.name,
39
+ description: t.description,
40
+ parameters: sanitizeGeminiSchema(t.input_schema)
41
+ }))
42
+ }]
43
+ };
44
+ default:
45
+ return {
46
+ tools: tools.map((t) => ({
47
+ type: "function",
48
+ function: { name: t.name, description: t.description, parameters: t.input_schema }
49
+ }))
50
+ };
51
+ }
52
+ }
53
+ function parseClaudeToolCalls(data) {
54
+ const content = data.content;
55
+ const stopReason = data.stop_reason || "end_turn";
56
+ const toolCalls = [];
57
+ let textContent = "";
58
+ if (content) {
59
+ for (const block of content) {
60
+ if (block.type === "tool_use") {
61
+ toolCalls.push({ id: block.id, name: block.name, arguments: block.input || {} });
62
+ } else if (block.type === "text") {
63
+ textContent += block.text;
64
+ }
65
+ }
66
+ }
67
+ return { toolCalls, textContent, stopReason };
68
+ }
69
+ function parseOpenAIToolCalls(data) {
70
+ const choices = data.choices;
71
+ if (!choices || choices.length === 0) return { toolCalls: [], textContent: "", stopReason: "stop" };
72
+ const choice = choices[0];
73
+ const message = choice.message;
74
+ const finishReason = choice.finish_reason || "stop";
75
+ const textContent = message?.content || "";
76
+ const toolCalls = [];
77
+ const rawToolCalls = message?.tool_calls;
78
+ if (rawToolCalls) {
79
+ for (const tc of rawToolCalls) {
80
+ const fn = tc.function;
81
+ let args = {};
82
+ try {
83
+ args = JSON.parse(fn.arguments);
84
+ } catch {
85
+ }
86
+ toolCalls.push({ id: tc.id, name: fn.name, arguments: args });
87
+ }
88
+ }
89
+ return { toolCalls, textContent, stopReason: finishReason === "tool_calls" ? "tool_use" : finishReason };
90
+ }
91
+ function parseGeminiToolCalls(data) {
92
+ const candidates = data.candidates;
93
+ if (!candidates || candidates.length === 0) return { toolCalls: [], textContent: "", stopReason: "stop" };
94
+ const content = candidates[0].content;
95
+ const parts = content?.parts;
96
+ const toolCalls = [];
97
+ let textContent = "";
98
+ if (parts) {
99
+ for (const part of parts) {
100
+ if (part.functionCall) {
101
+ const fc = part.functionCall;
102
+ const thoughtSignature = part.thoughtSignature ?? fc.thoughtSignature;
103
+ toolCalls.push({
104
+ id: `gemini-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,
105
+ name: fc.name,
106
+ arguments: fc.args || {},
107
+ ...thoughtSignature ? { providerMeta: { thoughtSignature } } : {}
108
+ });
109
+ } else if (part.text) {
110
+ textContent += part.text;
111
+ }
112
+ }
113
+ }
114
+ return { toolCalls, textContent, stopReason: toolCalls.length > 0 ? "tool_use" : "stop" };
115
+ }
116
+ function buildClaudeToolResultMessages(toolResults) {
117
+ return {
118
+ role: "user",
119
+ content: toolResults.map((r) => ({
120
+ type: "tool_result",
121
+ tool_use_id: r.callId,
122
+ content: r.content,
123
+ is_error: r.isError || false
124
+ }))
125
+ };
126
+ }
127
+ function buildOpenAIToolResultMessages(toolResults) {
128
+ return toolResults.map((r) => ({ role: "tool", tool_call_id: r.callId, content: r.content }));
129
+ }
130
+ export {
131
+ buildClaudeToolResultMessages,
132
+ buildOpenAIToolResultMessages,
133
+ formatToolsForProvider,
134
+ parseClaudeToolCalls,
135
+ parseGeminiToolCalls,
136
+ parseOpenAIToolCalls
137
+ };
138
+ //# sourceMappingURL=tool-bridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ai/drivers/tool-bridge.ts"],"sourcesContent":["/**\n * Tool formatting + response parsing, shared by all consumers.\n * Ported verbatim from the desktop app's tool-bridge (MCP-specific glue like\n * `mcpToolsToToolDefs` stays in the desktop repo — it has no place in core).\n */\nimport type { AIProvider, ToolDefinition, ToolCallRequest } from '../types'\n\nconst GEMINI_UNSUPPORTED_KEYS = new Set([\n 'additionalProperties', '$schema', '$id', '$ref', '$defs', 'definitions',\n 'patternProperties', 'unevaluatedProperties', 'dependentRequired',\n 'dependentSchemas', 'const',\n])\n\nfunction sanitizeGeminiSchema(schema: unknown): unknown {\n if (Array.isArray(schema)) return schema.map(sanitizeGeminiSchema)\n if (schema && typeof schema === 'object') {\n const out: Record<string, unknown> = {}\n for (const [key, value] of Object.entries(schema as Record<string, unknown>)) {\n if (GEMINI_UNSUPPORTED_KEYS.has(key)) continue\n out[key] = sanitizeGeminiSchema(value)\n }\n return out\n }\n return schema\n}\n\n/** Format tools into provider-specific request-body fields (merge into body). */\nexport function formatToolsForProvider(\n provider: AIProvider,\n tools: ToolDefinition[],\n): Record<string, unknown> {\n if (tools.length === 0) return {}\n switch (provider) {\n case 'claude':\n return {\n tools: tools.map(t => ({ name: t.name, description: t.description, input_schema: t.input_schema })),\n }\n case 'gemini':\n return {\n tools: [{\n functionDeclarations: tools.map(t => ({\n name: t.name,\n description: t.description,\n parameters: sanitizeGeminiSchema(t.input_schema),\n })),\n }],\n }\n default:\n // OpenAI-compatible (openai/deepseek/grok/mistral/glm/minimax/doubao/custom/ollama)\n return {\n tools: tools.map(t => ({\n type: 'function',\n function: { name: t.name, description: t.description, parameters: t.input_schema },\n })),\n }\n }\n}\n\nexport function parseClaudeToolCalls(data: Record<string, unknown>): {\n toolCalls: ToolCallRequest[]; textContent: string; stopReason: string\n} {\n const content = data.content as Array<Record<string, unknown>> | undefined\n const stopReason = (data.stop_reason as string) || 'end_turn'\n const toolCalls: ToolCallRequest[] = []\n let textContent = ''\n if (content) {\n for (const block of content) {\n if (block.type === 'tool_use') {\n toolCalls.push({ id: block.id as string, name: block.name as string, arguments: (block.input as Record<string, unknown>) || {} })\n } else if (block.type === 'text') {\n textContent += block.text as string\n }\n }\n }\n return { toolCalls, textContent, stopReason }\n}\n\nexport function parseOpenAIToolCalls(data: Record<string, unknown>): {\n toolCalls: ToolCallRequest[]; textContent: string; stopReason: string\n} {\n const choices = data.choices as Array<Record<string, unknown>> | undefined\n if (!choices || choices.length === 0) return { toolCalls: [], textContent: '', stopReason: 'stop' }\n const choice = choices[0]!\n const message = choice.message as Record<string, unknown> | undefined\n const finishReason = (choice.finish_reason as string) || 'stop'\n const textContent = (message?.content as string) || ''\n const toolCalls: ToolCallRequest[] = []\n const rawToolCalls = message?.tool_calls as Array<Record<string, unknown>> | undefined\n if (rawToolCalls) {\n for (const tc of rawToolCalls) {\n const fn = tc.function as Record<string, unknown>\n let args: Record<string, unknown> = {}\n try { args = JSON.parse(fn.arguments as string) } catch { /* truncated */ }\n toolCalls.push({ id: tc.id as string, name: fn.name as string, arguments: args })\n }\n }\n return { toolCalls, textContent, stopReason: finishReason === 'tool_calls' ? 'tool_use' : finishReason }\n}\n\nexport function parseGeminiToolCalls(data: Record<string, unknown>): {\n toolCalls: ToolCallRequest[]; textContent: string; stopReason: string\n} {\n const candidates = data.candidates as Array<Record<string, unknown>> | undefined\n if (!candidates || candidates.length === 0) return { toolCalls: [], textContent: '', stopReason: 'stop' }\n const content = candidates[0]!.content as Record<string, unknown> | undefined\n const parts = content?.parts as Array<Record<string, unknown>> | undefined\n const toolCalls: ToolCallRequest[] = []\n let textContent = ''\n if (parts) {\n for (const part of parts) {\n if (part.functionCall) {\n const fc = part.functionCall as Record<string, unknown>\n const thoughtSignature =\n (part.thoughtSignature as string | undefined) ?? (fc.thoughtSignature as string | undefined)\n toolCalls.push({\n id: `gemini-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`,\n name: fc.name as string,\n arguments: (fc.args as Record<string, unknown>) || {},\n ...(thoughtSignature ? { providerMeta: { thoughtSignature } } : {}),\n })\n } else if (part.text) {\n textContent += part.text as string\n }\n }\n }\n return { toolCalls, textContent, stopReason: toolCalls.length > 0 ? 'tool_use' : 'stop' }\n}\n\nexport function buildClaudeToolResultMessages(\n toolResults: Array<{ callId: string; content: string; isError?: boolean }>,\n): Record<string, unknown> {\n return {\n role: 'user',\n content: toolResults.map(r => ({\n type: 'tool_result', tool_use_id: r.callId, content: r.content, is_error: r.isError || false,\n })),\n }\n}\n\nexport function buildOpenAIToolResultMessages(\n toolResults: Array<{ callId: string; name: string; content: string }>,\n): Array<Record<string, unknown>> {\n return toolResults.map(r => ({ role: 'tool', tool_call_id: r.callId, content: r.content }))\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,2 @@
1
+ import '../types.js';
2
+ export { A as AIDriver, S as StreamFold } from '../../types-CwM77g7u.js';
@@ -0,0 +1 @@
1
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
@@ -0,0 +1,13 @@
1
+ import { AIProviderConfig } from '../types.js';
2
+
3
+ declare function resolveBaseUrl(config: AIProviderConfig, fallback: string): string;
4
+ /** Build an OpenAI-compatible endpoint, avoiding a double version prefix. */
5
+ declare function openaiEndpoint(baseUrl: string, path: string): string;
6
+ declare const NOOP_FOLD: {
7
+ pushEnvelope(): undefined;
8
+ finish(): {
9
+ stopReason: string;
10
+ };
11
+ };
12
+
13
+ export { NOOP_FOLD, openaiEndpoint, resolveBaseUrl };
@@ -0,0 +1,40 @@
1
+ // src/ai/catalog.ts
2
+ var PROVIDER_BASE_URLS = {
3
+ claude: "https://api.anthropic.com",
4
+ openai: "https://api.openai.com",
5
+ gemini: "https://generativelanguage.googleapis.com",
6
+ deepseek: "https://api.deepseek.com",
7
+ ollama: "http://localhost:11434",
8
+ grok: "https://api.x.ai",
9
+ mistral: "https://api.mistral.ai",
10
+ glm: "https://open.bigmodel.cn/api/paas/v4",
11
+ minimax: "https://api.minimax.io/v1",
12
+ doubao: "https://ark.cn-beijing.volces.com/api/v3",
13
+ custom: "",
14
+ "local-mlx": "",
15
+ "local-llama": ""
16
+ };
17
+
18
+ // src/ai/drivers/util.ts
19
+ function resolveBaseUrl(config, fallback) {
20
+ return config.baseUrl || PROVIDER_BASE_URLS[config.provider] || fallback;
21
+ }
22
+ function openaiEndpoint(baseUrl, path) {
23
+ const clean = baseUrl.replace(/\/+$/, "");
24
+ if (/\/v\d+$/.test(clean)) return `${clean}${path}`;
25
+ return `${clean}/v1${path}`;
26
+ }
27
+ var NOOP_FOLD = {
28
+ pushEnvelope() {
29
+ return void 0;
30
+ },
31
+ finish() {
32
+ return { stopReason: "end_turn" };
33
+ }
34
+ };
35
+ export {
36
+ NOOP_FOLD,
37
+ openaiEndpoint,
38
+ resolveBaseUrl
39
+ };
40
+ //# sourceMappingURL=util.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/ai/catalog.ts","../../../src/ai/drivers/util.ts"],"sourcesContent":["/**\n * Provider catalog — default models, base URLs, labels, id aliases.\n * Baselined on the desktop app. Pure data; no host imports.\n */\nimport type { AIProvider } from './types'\n\nexport const DEFAULT_MODELS: Record<AIProvider, string[]> = {\n claude: ['claude-opus-4-6', 'claude-sonnet-4-6', 'claude-haiku-4-5-20251001'],\n openai: ['gpt-5.2', 'gpt-5.2-pro', 'gpt-5', 'gpt-5-mini', 'o4-mini', 'gpt-4o', 'gpt-4o-mini', 'o3', 'o3-mini'],\n gemini: ['gemini-3.1-pro-preview', 'gemini-3-flash-preview', 'gemini-2.5-flash', 'gemini-2.5-flash-lite', 'gemini-2.0-pro-exp'],\n deepseek: ['deepseek-chat', 'deepseek-reasoner'],\n ollama: ['llama3.3', 'llama3.2', 'qwen2.5', 'qwen2.5-coder', 'phi4', 'gemma3', 'deepseek-r1', 'mistral', 'codellama'],\n grok: ['grok-4', 'grok-4-1-fast-reasoning', 'grok-4-1-fast-non-reasoning', 'grok-code-fast-1', 'grok-3'],\n mistral: ['mistral-large-latest', 'mistral-small-latest', 'magistral-medium-latest', 'magistral-small-latest', 'codestral-latest', 'devstral-latest'],\n glm: ['glm-5', 'glm-4-plus', 'glm-4-air', 'glm-4-flash', 'glm-z1-flash', 'glm-z1-air'],\n minimax: ['MiniMax-M2.5', 'MiniMax-M2.5-highspeed', 'MiniMax-Text-01'],\n doubao: [],\n custom: [],\n 'local-mlx': ['Qwen2.5-1.5B-Instruct-4bit'],\n 'local-llama': ['qwen2.5-1.5b-instruct-q4'],\n}\n\nexport const PROVIDER_BASE_URLS: Record<AIProvider, string> = {\n claude: 'https://api.anthropic.com',\n openai: 'https://api.openai.com',\n gemini: 'https://generativelanguage.googleapis.com',\n deepseek: 'https://api.deepseek.com',\n ollama: 'http://localhost:11434',\n grok: 'https://api.x.ai',\n mistral: 'https://api.mistral.ai',\n glm: 'https://open.bigmodel.cn/api/paas/v4',\n minimax: 'https://api.minimax.io/v1',\n doubao: 'https://ark.cn-beijing.volces.com/api/v3',\n custom: '',\n 'local-mlx': '',\n 'local-llama': '',\n}\n\nexport const PROVIDER_LABELS: Record<AIProvider, string> = {\n claude: 'Claude',\n openai: 'OpenAI',\n gemini: 'Gemini',\n deepseek: 'DeepSeek',\n ollama: 'Ollama',\n grok: 'Grok',\n mistral: 'Mistral',\n glm: 'GLM',\n minimax: 'MiniMax',\n doubao: 'Doubao',\n custom: 'Custom (OpenAI-compatible)',\n 'local-mlx': 'On-device (iOS)',\n 'local-llama': 'On-device (Android)',\n}\n\n/** Legacy/foreign provider ids → canonical `AIProvider`. */\nexport const PROVIDER_ALIASES: Record<string, AIProvider> = {\n anthropic: 'claude',\n}\n\n/** Normalize an incoming provider id (applies aliases). */\nexport function normalizeProvider(id: string): AIProvider {\n return (PROVIDER_ALIASES[id] ?? id) as AIProvider\n}\n","import { PROVIDER_BASE_URLS } from '../catalog'\nimport type { AIProviderConfig } from '../types'\n\nexport function resolveBaseUrl(config: AIProviderConfig, fallback: string): string {\n return config.baseUrl || PROVIDER_BASE_URLS[config.provider] || fallback\n}\n\n/** Build an OpenAI-compatible endpoint, avoiding a double version prefix. */\nexport function openaiEndpoint(baseUrl: string, path: string): string {\n const clean = baseUrl.replace(/\\/+$/, '')\n if (/\\/v\\d+$/.test(clean)) return `${clean}${path}`\n return `${clean}/v1${path}`\n}\n\nexport const NOOP_FOLD = {\n pushEnvelope() { return undefined },\n finish() { return { stopReason: 'end_turn' } },\n}\n"],"mappings":";AAsBO,IAAM,qBAAiD;AAAA,EAC5D,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,UAAU;AAAA,EACV,QAAQ;AAAA,EACR,MAAM;AAAA,EACN,SAAS;AAAA,EACT,KAAK;AAAA,EACL,SAAS;AAAA,EACT,QAAQ;AAAA,EACR,QAAQ;AAAA,EACR,aAAa;AAAA,EACb,eAAe;AACjB;;;ACjCO,SAAS,eAAe,QAA0B,UAA0B;AACjF,SAAO,OAAO,WAAW,mBAAmB,OAAO,QAAQ,KAAK;AAClE;AAGO,SAAS,eAAe,SAAiB,MAAsB;AACpE,QAAM,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AACxC,MAAI,UAAU,KAAK,KAAK,EAAG,QAAO,GAAG,KAAK,GAAG,IAAI;AACjD,SAAO,GAAG,KAAK,MAAM,IAAI;AAC3B;AAEO,IAAM,YAAY;AAAA,EACvB,eAAe;AAAE,WAAO;AAAA,EAAU;AAAA,EAClC,SAAS;AAAE,WAAO,EAAE,YAAY,WAAW;AAAA,EAAE;AAC/C;","names":[]}
@@ -0,0 +1,37 @@
1
+ interface GeneratedImage {
2
+ /** Public URL (caller may re-host). */
3
+ url?: string;
4
+ /** Base64 image bytes (no `data:` prefix). */
5
+ b64Json?: string;
6
+ /** Provider's safety/clarity-revised prompt, if any. */
7
+ revisedPrompt?: string;
8
+ }
9
+ interface ImageGenRequest {
10
+ prompt: string;
11
+ n?: number;
12
+ size?: string;
13
+ model?: string;
14
+ responseFormat?: 'url' | 'b64_json';
15
+ signal?: AbortSignal;
16
+ }
17
+ interface ImageGenResult {
18
+ provider: string;
19
+ model: string;
20
+ images: GeneratedImage[];
21
+ durationMs: number;
22
+ }
23
+ /** OpenAI-compatible `/images/generations` endpoint (avoids double /v1). */
24
+ declare function imageEndpoint(baseUrl: string): string;
25
+ /** Build the OpenAI-compatible images request body. `n` is included only when
26
+ * the caller passes it (already clamped per its model/capabilities) — some
27
+ * models (e.g. Doubao Seedream) require `n` to be absent. */
28
+ declare function buildOpenAIImageBody(model: string, req: {
29
+ prompt: string;
30
+ n?: number;
31
+ size?: string;
32
+ responseFormat?: 'url' | 'b64_json';
33
+ }): Record<string, unknown>;
34
+ /** Extract images from an OpenAI-compatible `{ data: [...] }` response. */
35
+ declare function extractOpenAIImages(json: unknown): GeneratedImage[];
36
+
37
+ export { type GeneratedImage, type ImageGenRequest, type ImageGenResult, buildOpenAIImageBody, extractOpenAIImages, imageEndpoint };
@@ -0,0 +1,36 @@
1
+ // src/ai/drivers/util.ts
2
+ function openaiEndpoint(baseUrl, path) {
3
+ const clean = baseUrl.replace(/\/+$/, "");
4
+ if (/\/v\d+$/.test(clean)) return `${clean}${path}`;
5
+ return `${clean}/v1${path}`;
6
+ }
7
+
8
+ // src/ai/image.ts
9
+ function imageEndpoint(baseUrl) {
10
+ return openaiEndpoint(baseUrl, "/images/generations");
11
+ }
12
+ function buildOpenAIImageBody(model, req) {
13
+ const body = {
14
+ model,
15
+ prompt: req.prompt,
16
+ response_format: req.responseFormat ?? "url"
17
+ };
18
+ if (req.n !== void 0) body.n = req.n;
19
+ if (req.size) body.size = req.size;
20
+ return body;
21
+ }
22
+ function extractOpenAIImages(json) {
23
+ const data = json.data;
24
+ if (!Array.isArray(data)) return [];
25
+ return data.map((d) => ({
26
+ ...d.url ? { url: d.url } : {},
27
+ ...d.b64_json ? { b64Json: d.b64_json } : {},
28
+ ...d.revised_prompt ? { revisedPrompt: d.revised_prompt } : {}
29
+ }));
30
+ }
31
+ export {
32
+ buildOpenAIImageBody,
33
+ extractOpenAIImages,
34
+ imageEndpoint
35
+ };
36
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/ai/drivers/util.ts","../../src/ai/image.ts"],"sourcesContent":["import { PROVIDER_BASE_URLS } from '../catalog'\nimport type { AIProviderConfig } from '../types'\n\nexport function resolveBaseUrl(config: AIProviderConfig, fallback: string): string {\n return config.baseUrl || PROVIDER_BASE_URLS[config.provider] || fallback\n}\n\n/** Build an OpenAI-compatible endpoint, avoiding a double version prefix. */\nexport function openaiEndpoint(baseUrl: string, path: string): string {\n const clean = baseUrl.replace(/\\/+$/, '')\n if (/\\/v\\d+$/.test(clean)) return `${clean}${path}`\n return `${clean}/v1${path}`\n}\n\nexport const NOOP_FOLD = {\n pushEnvelope() { return undefined },\n finish() { return { stopReason: 'end_turn' } },\n}\n","/**\n * Shared image generation — the OpenAI-compatible `/images/generations` path\n * used by every Moraya image provider that speaks that protocol (web's\n * openai-image + doubao-image; desktop's openai/grok/doubao/custom). The\n * request body + response extraction live here once; each app keeps its own\n * transport (fetch vs Tauri proxy), key handling, guards, and error mapping.\n *\n * Desktop's gemini-predict + qwen/DashScope two-phase providers are NOT here —\n * they stay in the desktop app (different protocols, no web counterpart).\n */\nimport { openaiEndpoint } from './drivers/util'\n\nexport interface GeneratedImage {\n /** Public URL (caller may re-host). */\n url?: string\n /** Base64 image bytes (no `data:` prefix). */\n b64Json?: string\n /** Provider's safety/clarity-revised prompt, if any. */\n revisedPrompt?: string\n}\n\nexport interface ImageGenRequest {\n prompt: string\n n?: number\n size?: string\n model?: string\n responseFormat?: 'url' | 'b64_json'\n signal?: AbortSignal\n}\n\nexport interface ImageGenResult {\n provider: string\n model: string\n images: GeneratedImage[]\n durationMs: number\n}\n\n/** OpenAI-compatible `/images/generations` endpoint (avoids double /v1). */\nexport function imageEndpoint(baseUrl: string): string {\n return openaiEndpoint(baseUrl, '/images/generations')\n}\n\n/** Build the OpenAI-compatible images request body. `n` is included only when\n * the caller passes it (already clamped per its model/capabilities) — some\n * models (e.g. Doubao Seedream) require `n` to be absent. */\nexport function buildOpenAIImageBody(\n model: string,\n req: { prompt: string; n?: number; size?: string; responseFormat?: 'url' | 'b64_json' },\n): Record<string, unknown> {\n const body: Record<string, unknown> = {\n model,\n prompt: req.prompt,\n response_format: req.responseFormat ?? 'url',\n }\n if (req.n !== undefined) body.n = req.n\n if (req.size) body.size = req.size\n return body\n}\n\n/** Extract images from an OpenAI-compatible `{ data: [...] }` response. */\nexport function extractOpenAIImages(json: unknown): GeneratedImage[] {\n const data = (json as { data?: Array<{ url?: string; b64_json?: string; revised_prompt?: string }> }).data\n if (!Array.isArray(data)) return []\n return data.map(d => ({\n ...(d.url ? { url: d.url } : {}),\n ...(d.b64_json ? { b64Json: d.b64_json } : {}),\n ...(d.revised_prompt ? { revisedPrompt: d.revised_prompt } : {}),\n }))\n}\n"],"mappings":";AAQO,SAAS,eAAe,SAAiB,MAAsB;AACpE,QAAM,QAAQ,QAAQ,QAAQ,QAAQ,EAAE;AACxC,MAAI,UAAU,KAAK,KAAK,EAAG,QAAO,GAAG,KAAK,GAAG,IAAI;AACjD,SAAO,GAAG,KAAK,MAAM,IAAI;AAC3B;;;AC0BO,SAAS,cAAc,SAAyB;AACrD,SAAO,eAAe,SAAS,qBAAqB;AACtD;AAKO,SAAS,qBACd,OACA,KACyB;AACzB,QAAM,OAAgC;AAAA,IACpC;AAAA,IACA,QAAQ,IAAI;AAAA,IACZ,iBAAiB,IAAI,kBAAkB;AAAA,EACzC;AACA,MAAI,IAAI,MAAM,OAAW,MAAK,IAAI,IAAI;AACtC,MAAI,IAAI,KAAM,MAAK,OAAO,IAAI;AAC9B,SAAO;AACT;AAGO,SAAS,oBAAoB,MAAiC;AACnE,QAAM,OAAQ,KAAwF;AACtG,MAAI,CAAC,MAAM,QAAQ,IAAI,EAAG,QAAO,CAAC;AAClC,SAAO,KAAK,IAAI,QAAM;AAAA,IACpB,GAAI,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,IAAI,CAAC;AAAA,IAC9B,GAAI,EAAE,WAAW,EAAE,SAAS,EAAE,SAAS,IAAI,CAAC;AAAA,IAC5C,GAAI,EAAE,iBAAiB,EAAE,eAAe,EAAE,eAAe,IAAI,CAAC;AAAA,EAChE,EAAE;AACJ;","names":[]}
@@ -0,0 +1,37 @@
1
+ import { AIProvider, AIProviderConfig, AIRequest, AIResponse, AIStreamEvent } from './types.js';
2
+ export { AIUsage, ChatMessage, ImageAttachment, StreamToolResult, ToolCallRequest, ToolDefinition } from './types.js';
3
+ import { a as AITransport } from '../types-CwM77g7u.js';
4
+ export { A as AIDriver, b as AuthDescriptor, c as StreamCallbacks, S as StreamFold, T as TransportRequest, d as TransportResponse } from '../types-CwM77g7u.js';
5
+ export { getDriver } from './drivers/index.js';
6
+ export { buildClaudeToolResultMessages, buildOpenAIToolResultMessages, formatToolsForProvider, parseClaudeToolCalls, parseGeminiToolCalls, parseOpenAIToolCalls } from './drivers/tool-bridge.js';
7
+ export { openaiEndpoint } from './drivers/util.js';
8
+ export { GeneratedImage, ImageGenRequest, ImageGenResult, buildOpenAIImageBody, extractOpenAIImages, imageEndpoint } from './image.js';
9
+ export { REALTIME_VOICE_BASE_URLS, REALTIME_VOICE_DEFAULT_MODELS, REALTIME_VOICE_PROVIDER_NAMES, RealtimeVoiceAIConfig, RealtimeVoiceProvider, SpeechProvider, SpeechProviderConfig } from './voice.js';
10
+ import './drivers/claude.js';
11
+ import './drivers/openai.js';
12
+ import './drivers/gemini.js';
13
+ import './drivers/ollama.js';
14
+
15
+ /**
16
+ * Provider catalog — default models, base URLs, labels, id aliases.
17
+ * Baselined on the desktop app. Pure data; no host imports.
18
+ */
19
+
20
+ declare const DEFAULT_MODELS: Record<AIProvider, string[]>;
21
+ declare const PROVIDER_BASE_URLS: Record<AIProvider, string>;
22
+ declare const PROVIDER_LABELS: Record<AIProvider, string>;
23
+ /** Legacy/foreign provider ids → canonical `AIProvider`. */
24
+ declare const PROVIDER_ALIASES: Record<string, AIProvider>;
25
+ /** Normalize an incoming provider id (applies aliases). */
26
+ declare function normalizeProvider(id: string): AIProvider;
27
+
28
+ /**
29
+ * Chat orchestrator — composes a per-provider driver with a platform transport.
30
+ * Platform-agnostic: no fetch, no Tauri. Streaming uses a small callback→async
31
+ * generator pump (ported from the desktop streamViaProxy backpressure logic).
32
+ */
33
+
34
+ declare function sendChat(config: AIProviderConfig, request: AIRequest, transport: AITransport, signal?: AbortSignal): Promise<AIResponse>;
35
+ declare function streamChat(config: AIProviderConfig, request: AIRequest, transport: AITransport, signal?: AbortSignal): AsyncGenerator<AIStreamEvent>;
36
+
37
+ export { AIProvider, AIProviderConfig, AIRequest, AIResponse, AIStreamEvent, AITransport, DEFAULT_MODELS, PROVIDER_ALIASES, PROVIDER_BASE_URLS, PROVIDER_LABELS, normalizeProvider, sendChat, streamChat };