@lowire/loop 0.0.1 → 0.0.2

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,23 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type * as types from '../types';
17
+ export declare class Anthropic implements types.Provider {
18
+ readonly name = "anthropic";
19
+ complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
20
+ result: types.AssistantMessage;
21
+ usage: types.Usage;
22
+ }>;
23
+ }
@@ -0,0 +1,179 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.Anthropic = void 0;
19
+ class Anthropic {
20
+ name = 'anthropic';
21
+ async complete(conversation, options) {
22
+ const response = await create({
23
+ model: options.model,
24
+ max_tokens: options.maxTokens ?? 32768,
25
+ temperature: options.temperature,
26
+ system: systemPrompt(conversation.systemPrompt),
27
+ messages: conversation.messages.map(toAnthropicMessagePart),
28
+ tools: conversation.tools.map(toAnthropicTool),
29
+ thinking: options.reasoning ? {
30
+ type: 'enabled',
31
+ budget_tokens: options.maxTokens ? Math.round(options.maxTokens / 10) : 1024,
32
+ } : undefined,
33
+ });
34
+ const result = toAssistantMessage(response);
35
+ const usage = {
36
+ input: response.usage.input_tokens,
37
+ output: response.usage.output_tokens,
38
+ };
39
+ return { result, usage };
40
+ }
41
+ }
42
+ exports.Anthropic = Anthropic;
43
+ async function create(body) {
44
+ const headers = {
45
+ 'Content-Type': 'application/json',
46
+ 'x-api-key': process.env.ANTHROPIC_API_KEY,
47
+ 'anthropic-version': '2023-06-01',
48
+ };
49
+ const response = await fetch(`https://api.anthropic.com/v1/messages`, {
50
+ method: 'POST',
51
+ headers,
52
+ body: JSON.stringify(body)
53
+ });
54
+ if (!response.ok)
55
+ throw new Error(`API error: ${response.status} ${response.statusText} ${await response.text()}`);
56
+ return await response.json();
57
+ }
58
+ function toContentPart(block) {
59
+ if (block.type === 'text') {
60
+ return {
61
+ type: 'text',
62
+ text: block.text,
63
+ };
64
+ }
65
+ if (block.type === 'tool_use') {
66
+ return {
67
+ type: 'tool_call',
68
+ name: block.name,
69
+ arguments: block.input,
70
+ id: block.id,
71
+ };
72
+ }
73
+ if (block.type === 'thinking') {
74
+ return {
75
+ type: 'thinking',
76
+ thinking: block.thinking,
77
+ signature: block.signature,
78
+ };
79
+ }
80
+ return null;
81
+ }
82
+ function toAnthropicResultParam(part) {
83
+ if (part.type === 'text') {
84
+ return {
85
+ type: 'text',
86
+ text: part.text,
87
+ };
88
+ }
89
+ if (part.type === 'image') {
90
+ return {
91
+ type: 'image',
92
+ source: {
93
+ type: 'base64',
94
+ data: part.data,
95
+ media_type: part.mimeType
96
+ },
97
+ };
98
+ }
99
+ throw new Error(`Unsupported content part type: ${part.type}`);
100
+ }
101
+ function toAssistantMessage(message) {
102
+ return {
103
+ role: 'assistant',
104
+ content: message.content.map(toContentPart).filter(Boolean),
105
+ };
106
+ }
107
+ function toAnthropicTool(tool) {
108
+ return {
109
+ name: tool.name,
110
+ description: tool.description,
111
+ input_schema: tool.inputSchema,
112
+ };
113
+ }
114
+ function toAnthropicAssistantMessageParam(message) {
115
+ const content = [];
116
+ for (const part of message.content) {
117
+ if (part.type === 'text') {
118
+ content.push({ ...part, citations: [] });
119
+ continue;
120
+ }
121
+ if (part.type === 'tool_call') {
122
+ content.push({
123
+ type: 'tool_use',
124
+ id: part.id,
125
+ name: part.name,
126
+ input: part.arguments
127
+ });
128
+ continue;
129
+ }
130
+ if (part.type === 'thinking') {
131
+ content.push({
132
+ type: 'thinking',
133
+ thinking: part.thinking,
134
+ signature: part.signature,
135
+ });
136
+ continue;
137
+ }
138
+ }
139
+ return {
140
+ role: 'assistant',
141
+ content
142
+ };
143
+ }
144
+ function toAnthropicToolResultMessage(message) {
145
+ const toolResult = {
146
+ type: 'tool_result',
147
+ tool_use_id: message.toolCallId,
148
+ content: message.result.content.map(toAnthropicResultParam),
149
+ is_error: message.result.isError,
150
+ };
151
+ return {
152
+ role: 'user',
153
+ content: [toolResult]
154
+ };
155
+ }
156
+ function toAnthropicMessagePart(message) {
157
+ if (message.role === 'user') {
158
+ return {
159
+ role: 'user',
160
+ content: message.content
161
+ };
162
+ }
163
+ if (message.role === 'assistant')
164
+ return toAnthropicAssistantMessageParam(message);
165
+ if (message.role === 'tool_result')
166
+ return toAnthropicToolResultMessage(message);
167
+ throw new Error(`Unsupported message role: ${message.role}`);
168
+ }
169
+ const systemPrompt = (prompt) => `
170
+ ### System instructions
171
+
172
+ ${prompt}
173
+
174
+ ### Tool calling instructions
175
+ - Make sure every message contains a tool call.
176
+ - When you use a tool, you may provide a brief thought or explanation in the content field
177
+ immediately before the tool_call. Do not split this into separate messages.
178
+ - Every reply must include a tool call.
179
+ `;
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import { OpenAICompletions } from './openaiCompletions';
17
+ import type { Endpoint } from './openaiCompletions';
18
+ import type * as types from '../types';
19
+ export declare const kEditorHeaders: {
20
+ 'Editor-Version': string;
21
+ 'Editor-Plugin-Version': string;
22
+ 'User-Agent': string;
23
+ Accept: string;
24
+ 'Content-Type': string;
25
+ };
26
+ export declare class Github extends OpenAICompletions {
27
+ readonly name = "copilot";
28
+ connect(): Promise<Endpoint>;
29
+ complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
30
+ result: types.AssistantMessage;
31
+ usage: types.Usage;
32
+ }>;
33
+ }
@@ -0,0 +1,68 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.Github = exports.kEditorHeaders = void 0;
19
+ const openaiCompletions_1 = require("./openaiCompletions");
20
+ exports.kEditorHeaders = {
21
+ 'Editor-Version': 'vscode/1.96.0',
22
+ 'Editor-Plugin-Version': 'copilot-chat/0.24.0',
23
+ 'User-Agent': 'GitHubCopilotChat/0.24.0',
24
+ 'Accept': 'application/json',
25
+ 'Content-Type': 'application/json'
26
+ };
27
+ // Copilot endpoint does not reply with content+tool_call, it instead
28
+ // replies with the content and expects continuation. I.e. instead of navigating
29
+ // to a page it will reply with "Navigating to <url>" w/o tool call. Mitigate it
30
+ // via injecting a tool call intent and then converting it into the assistant
31
+ // message content.
32
+ class Github extends openaiCompletions_1.OpenAICompletions {
33
+ name = 'copilot';
34
+ async connect() {
35
+ return {
36
+ baseUrl: 'https://api.githubcopilot.com',
37
+ apiKey: await getCopilotToken(),
38
+ headers: exports.kEditorHeaders
39
+ };
40
+ }
41
+ async complete(conversation, options) {
42
+ const message = await super.complete(conversation, { ...options, injectIntent: true });
43
+ const textPart = message.result.content.find(part => part.type === 'text');
44
+ if (!textPart) {
45
+ const content = [];
46
+ const toolCalls = message.result.content.filter(part => part.type === 'tool_call');
47
+ for (const toolCall of toolCalls) {
48
+ content.push(toolCall.arguments?._intent ?? '');
49
+ delete toolCall.arguments._intent;
50
+ }
51
+ const text = content.join(' ').trim();
52
+ if (text.trim())
53
+ message.result.content.unshift({ type: 'text', text: content.join(' ') });
54
+ }
55
+ return message;
56
+ }
57
+ }
58
+ exports.Github = Github;
59
+ async function getCopilotToken() {
60
+ const response = await fetch('https://api.github.com/copilot_internal/v2/token', {
61
+ method: 'GET',
62
+ headers: { 'Authorization': `token ${process.env.COPILOT_API_KEY}`, ...exports.kEditorHeaders }
63
+ });
64
+ const data = await response.json();
65
+ if (data.token)
66
+ return data.token;
67
+ throw new Error('Failed to get Copilot token');
68
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type * as types from '../types';
17
+ export declare class Google implements types.Provider {
18
+ readonly name = "google";
19
+ complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
20
+ result: types.AssistantMessage;
21
+ usage: types.Usage;
22
+ }>;
23
+ }
@@ -0,0 +1,186 @@
1
+ "use strict";
2
+ /**
3
+ * Copyright (c) Microsoft Corporation.
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.Google = void 0;
19
+ class Google {
20
+ name = 'google';
21
+ async complete(conversation, options) {
22
+ const contents = conversation.messages.map(toGeminiContent).flat();
23
+ const response = await create(options.model ?? 'gemini-2.5-pro', {
24
+ systemInstruction: {
25
+ role: 'system',
26
+ parts: [
27
+ { text: systemPrompt(conversation.systemPrompt) }
28
+ ]
29
+ },
30
+ contents,
31
+ tools: conversation.tools.length > 0 ? [{ functionDeclarations: conversation.tools.map(toGeminiTool) }] : undefined,
32
+ generationConfig: { temperature: options.temperature },
33
+ });
34
+ const [candidate] = response.candidates ?? [];
35
+ if (!candidate)
36
+ throw new Error('No candidates in response');
37
+ const usage = {
38
+ input: response.usageMetadata?.promptTokenCount ?? 0,
39
+ output: response.usageMetadata?.candidatesTokenCount ?? 0,
40
+ };
41
+ const result = toAssistantMessage(candidate);
42
+ return { result, usage };
43
+ }
44
+ }
45
+ exports.Google = Google;
46
+ async function create(model, body) {
47
+ const apiKey = process.env.GEMINI_API_KEY ?? process.env.GOOGLE_API_KEY;
48
+ if (!apiKey)
49
+ throw new Error('GEMINI_API_KEY or GOOGLE_API_KEY environment variable is required');
50
+ const response = await fetch(`https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent`, {
51
+ method: 'POST',
52
+ headers: {
53
+ 'Content-Type': 'application/json',
54
+ 'x-goog-api-key': apiKey,
55
+ },
56
+ body: JSON.stringify(body)
57
+ });
58
+ if (!response.ok)
59
+ throw new Error(`API error: ${response.status} ${response.statusText} ${await response.text()}`);
60
+ return await response.json();
61
+ }
62
+ function toGeminiTool(tool) {
63
+ return {
64
+ name: tool.name,
65
+ description: tool.description,
66
+ parameters: stripUnsupportedSchemaFields(tool.inputSchema),
67
+ };
68
+ }
69
+ function stripUnsupportedSchemaFields(schema) {
70
+ if (!schema || typeof schema !== 'object')
71
+ return schema;
72
+ const cleaned = Array.isArray(schema) ? [...schema] : { ...schema };
73
+ delete cleaned.additionalProperties;
74
+ for (const key in cleaned) {
75
+ if (cleaned[key] && typeof cleaned[key] === 'object')
76
+ cleaned[key] = stripUnsupportedSchemaFields(cleaned[key]);
77
+ }
78
+ return cleaned;
79
+ }
80
+ function toAssistantMessage(candidate) {
81
+ return {
82
+ role: 'assistant',
83
+ content: candidate.content.parts.map(toContentPart).filter(Boolean),
84
+ };
85
+ }
86
+ function toContentPart(part) {
87
+ if (part.text) {
88
+ return {
89
+ type: 'text',
90
+ text: part.text,
91
+ googleThoughtSignature: part.thoughtSignature,
92
+ };
93
+ }
94
+ if (part.functionCall) {
95
+ return {
96
+ type: 'tool_call',
97
+ name: part.functionCall.name,
98
+ arguments: part.functionCall.args,
99
+ id: `call_${Math.random().toString(36).substring(2, 15)}`,
100
+ googleThoughtSignature: part.thoughtSignature,
101
+ };
102
+ }
103
+ return null;
104
+ }
105
+ function toGeminiContent(message) {
106
+ if (message.role === 'user') {
107
+ return [{
108
+ role: 'user',
109
+ parts: [{ text: message.content }]
110
+ }];
111
+ }
112
+ if (message.role === 'assistant') {
113
+ const parts = [];
114
+ for (const part of message.content) {
115
+ if (part.type === 'text') {
116
+ parts.push({
117
+ text: part.text,
118
+ thoughtSignature: part.googleThoughtSignature,
119
+ });
120
+ continue;
121
+ }
122
+ if (part.type === 'tool_call') {
123
+ parts.push({
124
+ functionCall: {
125
+ name: part.name,
126
+ args: part.arguments
127
+ },
128
+ thoughtSignature: part.googleThoughtSignature,
129
+ });
130
+ }
131
+ }
132
+ return [{
133
+ role: 'model',
134
+ parts
135
+ }];
136
+ }
137
+ if (message.role === 'tool_result') {
138
+ const responseContent = {};
139
+ const textParts = [];
140
+ const inlineDatas = [];
141
+ for (const part of message.result.content) {
142
+ if (part.type === 'text') {
143
+ textParts.push(part.text);
144
+ }
145
+ else if (part.type === 'image') {
146
+ // Store image data for inclusion in response
147
+ inlineDatas.push({
148
+ inline_data: {
149
+ mime_type: part.mimeType,
150
+ data: part.data
151
+ }
152
+ });
153
+ }
154
+ }
155
+ if (textParts.length > 0)
156
+ responseContent.result = textParts.join('\n');
157
+ const result = [{
158
+ role: 'function',
159
+ parts: [{
160
+ functionResponse: {
161
+ name: message.toolName,
162
+ response: responseContent
163
+ }
164
+ }]
165
+ }];
166
+ if (inlineDatas.length > 0) {
167
+ result.push({
168
+ role: 'user',
169
+ parts: inlineDatas
170
+ });
171
+ }
172
+ return result;
173
+ }
174
+ throw new Error(`Unsupported message role: ${message.role}`);
175
+ }
176
+ const systemPrompt = (prompt) => `
177
+ ### System instructions
178
+
179
+ ${prompt}
180
+
181
+ ### Tool calling instructions
182
+ - Make sure every message contains a tool call.
183
+ - When you use a tool, you may provide a brief thought or explanation in the content field
184
+ immediately before the tool_call. Do not split this into separate messages.
185
+ - Every reply must include a tool call.
186
+ `;
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Copyright (c) Microsoft Corporation.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+ import type * as types from '../types';
17
+ export declare class OpenAI implements types.Provider {
18
+ readonly name: string;
19
+ complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
20
+ result: types.AssistantMessage;
21
+ usage: types.Usage;
22
+ }>;
23
+ }