@lowire/loop 0.0.2 → 0.0.4
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/README.md +1 -1
- package/index.d.ts +2 -1
- package/index.js +2 -1
- package/index.mjs +1 -0
- package/lib/cache.d.ts +7 -2
- package/lib/cache.js +9 -10
- package/lib/jsx/jsx-runtime.d.ts +17 -0
- package/lib/jsx/jsx-runtime.js +33 -0
- package/lib/loop.d.ts +9 -18
- package/lib/loop.js +55 -67
- package/lib/{providers/openaiCompletions.d.ts → mcp/index.d.ts} +17 -16
- package/lib/mcp/index.js +109 -0
- package/lib/providers/anthropic.js +37 -21
- package/lib/providers/github.d.ts +10 -10
- package/lib/providers/github.js +195 -34
- package/lib/providers/google.js +58 -40
- package/lib/providers/openai.js +35 -21
- package/lib/summary.d.ts +20 -0
- package/lib/summary.js +56 -0
- package/lib/types.d.ts +39 -33
- package/package.json +1 -34
- package/lib/providers/openaiCompletions.js +0 -174
package/README.md
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
##
|
|
1
|
+
## lowire agentic loop
|
package/index.d.ts
CHANGED
package/index.js
CHANGED
package/index.mjs
CHANGED
package/lib/cache.d.ts
CHANGED
|
@@ -14,5 +14,10 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import type * as types from './types';
|
|
17
|
-
|
|
18
|
-
|
|
17
|
+
type ReplayCaches = {
|
|
18
|
+
input: types.ReplayCache;
|
|
19
|
+
output: types.ReplayCache;
|
|
20
|
+
secrets: Record<string, string>;
|
|
21
|
+
};
|
|
22
|
+
export declare function cachedComplete(provider: types.Provider, conversation: types.Conversation, caches: ReplayCaches | undefined, options: types.CompletionOptions): ReturnType<types.Provider['complete']>;
|
|
23
|
+
export {};
|
package/lib/cache.js
CHANGED
|
@@ -20,23 +20,22 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.cachedComplete = cachedComplete;
|
|
22
22
|
const crypto_1 = __importDefault(require("crypto"));
|
|
23
|
-
async function cachedComplete(provider, conversation, options) {
|
|
24
|
-
const caches = options.caches;
|
|
25
|
-
const secrets = options.secrets || {};
|
|
23
|
+
async function cachedComplete(provider, conversation, caches, options) {
|
|
26
24
|
if (!caches)
|
|
27
25
|
return await provider.complete(conversation, options);
|
|
26
|
+
const secrets = caches.secrets || {};
|
|
28
27
|
const c = hideSecrets(conversation, secrets);
|
|
29
28
|
const key = calculateSha1(JSON.stringify(c));
|
|
30
|
-
if (!process.env.
|
|
31
|
-
caches.
|
|
32
|
-
return unhideSecrets(caches.
|
|
29
|
+
if (!process.env.LOWIRE_NO_CACHE && caches.input[key]) {
|
|
30
|
+
caches.output[key] = caches.input[key];
|
|
31
|
+
return unhideSecrets(caches.input[key] ?? caches.output[key], secrets);
|
|
33
32
|
}
|
|
34
|
-
if (!process.env.
|
|
35
|
-
return unhideSecrets(caches.
|
|
36
|
-
if (process.env.
|
|
33
|
+
if (!process.env.LOWIRE_NO_CACHE && caches.output[key])
|
|
34
|
+
return unhideSecrets(caches.output[key], secrets);
|
|
35
|
+
if (process.env.LOWIRE_FORCE_CACHE)
|
|
37
36
|
throw new Error('Cache missing but TL_FORCE_CACHE is set' + JSON.stringify(conversation, null, 2));
|
|
38
37
|
const result = await provider.complete(conversation, options);
|
|
39
|
-
caches.
|
|
38
|
+
caches.output[key] = hideSecrets(result, secrets);
|
|
40
39
|
return result;
|
|
41
40
|
}
|
|
42
41
|
function hideSecrets(conversation, secrets) {
|
|
@@ -0,0 +1,17 @@
|
|
|
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
|
+
export declare function jsx(tag: string, props: Record<string, any> | null): string;
|
|
17
|
+
export declare const jsxs: typeof jsx;
|
|
@@ -0,0 +1,33 @@
|
|
|
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.jsxs = void 0;
|
|
19
|
+
exports.jsx = jsx;
|
|
20
|
+
function jsx(tag, props) {
|
|
21
|
+
const { children, ...rest } = props || {};
|
|
22
|
+
const attrs = Object.entries(rest);
|
|
23
|
+
const childArray = (Array.isArray(children) ? children.flat() : (children ? [children] : [])).filter(a => a && !!a.trim());
|
|
24
|
+
const lines = [`${tag}:`];
|
|
25
|
+
for (const [k, v] of attrs)
|
|
26
|
+
lines.push(` ${k}: ${v}`);
|
|
27
|
+
for (const child of childArray) {
|
|
28
|
+
const indented = child.split('\n').map((line) => ` ${line}`).join('\n');
|
|
29
|
+
lines.push(indented);
|
|
30
|
+
}
|
|
31
|
+
return lines.join('\n');
|
|
32
|
+
}
|
|
33
|
+
exports.jsxs = jsx;
|
package/lib/loop.d.ts
CHANGED
|
@@ -14,34 +14,25 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import type * as types from './types';
|
|
17
|
-
export type Logger = (category: string, text: string, details?: string) => void;
|
|
18
|
-
type Sizes = {
|
|
19
|
-
headers: number;
|
|
20
|
-
messages: number;
|
|
21
|
-
toolsResults: number;
|
|
22
|
-
};
|
|
23
17
|
export type LoopOptions = types.CompletionOptions & {
|
|
24
18
|
tools?: types.Tool[];
|
|
25
19
|
callTool?: types.ToolCallback;
|
|
26
20
|
maxTurns?: number;
|
|
27
21
|
resultSchema?: types.Schema;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
conversation: types.Conversation;
|
|
34
|
-
sizes: Sizes;
|
|
35
|
-
totalUsage: types.Usage;
|
|
36
|
-
}) => Promise<'stop' | undefined | void>;
|
|
22
|
+
cache?: {
|
|
23
|
+
messages: types.ReplayCache;
|
|
24
|
+
secrets: Record<string, string>;
|
|
25
|
+
};
|
|
26
|
+
summarize?: boolean;
|
|
37
27
|
};
|
|
38
28
|
export declare class Loop {
|
|
39
29
|
private _provider;
|
|
40
30
|
private _loopOptions;
|
|
31
|
+
private _cacheOutput;
|
|
41
32
|
constructor(loopName: 'openai' | 'github' | 'anthropic' | 'google', options: LoopOptions);
|
|
42
33
|
run<T>(task: string, runOptions?: Omit<LoopOptions, 'model'> & {
|
|
43
34
|
model?: string;
|
|
44
|
-
}): Promise<T
|
|
45
|
-
private
|
|
35
|
+
}): Promise<T>;
|
|
36
|
+
private _summarizeConversation;
|
|
37
|
+
cache(): types.ReplayCache;
|
|
46
38
|
}
|
|
47
|
-
export {};
|
package/lib/loop.js
CHANGED
|
@@ -18,23 +18,23 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
18
18
|
exports.Loop = void 0;
|
|
19
19
|
const registry_1 = require("./providers/registry");
|
|
20
20
|
const cache_1 = require("./cache");
|
|
21
|
+
const summary_1 = require("./summary");
|
|
21
22
|
class Loop {
|
|
22
23
|
_provider;
|
|
23
24
|
_loopOptions;
|
|
25
|
+
_cacheOutput = {};
|
|
24
26
|
constructor(loopName, options) {
|
|
25
27
|
this._provider = (0, registry_1.getProvider)(loopName);
|
|
26
28
|
this._loopOptions = options;
|
|
27
29
|
}
|
|
28
30
|
async run(task, runOptions = {}) {
|
|
29
31
|
const options = { ...this._loopOptions, ...runOptions };
|
|
30
|
-
const allTools = [
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
},
|
|
37
|
-
];
|
|
32
|
+
const allTools = [...options.tools || []];
|
|
33
|
+
allTools.push({
|
|
34
|
+
name: 'report_result',
|
|
35
|
+
description: 'Report the result of the task.',
|
|
36
|
+
inputSchema: options.resultSchema ?? defaultResultSchema,
|
|
37
|
+
});
|
|
38
38
|
const conversation = {
|
|
39
39
|
systemPrompt,
|
|
40
40
|
messages: [
|
|
@@ -42,98 +42,86 @@ class Loop {
|
|
|
42
42
|
],
|
|
43
43
|
tools: allTools,
|
|
44
44
|
};
|
|
45
|
-
const
|
|
45
|
+
const debug = options.debug;
|
|
46
46
|
const totalUsage = { input: 0, output: 0 };
|
|
47
|
-
|
|
47
|
+
debug?.('lowire:loop')(`Starting ${this._provider.name} loop`, task);
|
|
48
48
|
const maxTurns = options.maxTurns || 100;
|
|
49
49
|
for (let turn = 0; turn < maxTurns; ++turn) {
|
|
50
|
-
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
debug?.('lowire:loop')(`Turn ${turn + 1} of (max ${maxTurns})`);
|
|
51
|
+
const caches = options.cache ? {
|
|
52
|
+
input: options.cache.messages,
|
|
53
|
+
output: this._cacheOutput,
|
|
54
|
+
secrets: options.cache.secrets
|
|
55
|
+
} : undefined;
|
|
56
|
+
const summarizedConversation = options.summarize ? this._summarizeConversation(task, conversation, options) : conversation;
|
|
57
|
+
debug?.('lowire:loop')(`Request`, JSON.stringify({ ...summarizedConversation, tools: `${summarizedConversation.tools.length} tools` }, null, 2));
|
|
58
|
+
const { result: assistantMessage, usage } = await (0, cache_1.cachedComplete)(this._provider, summarizedConversation, caches, options);
|
|
59
|
+
const text = assistantMessage.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
|
|
60
|
+
debug?.('lowire:loop')('Usage', `input: ${usage.input}, output: ${usage.output}`);
|
|
61
|
+
debug?.('lowire:loop')('Assistant', text, JSON.stringify(assistantMessage.content, null, 2));
|
|
55
62
|
totalUsage.input += usage.input;
|
|
56
63
|
totalUsage.output += usage.output;
|
|
57
64
|
conversation.messages.push(assistantMessage);
|
|
58
|
-
const text = assistantMessage.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
|
|
59
65
|
const toolCalls = assistantMessage.content.filter(part => part.type === 'tool_call');
|
|
60
|
-
log('loop:usage', `input: ${usage.input}, output: ${usage.output}`);
|
|
61
|
-
log('loop:assistant', text, JSON.stringify(assistantMessage.content, null, 2));
|
|
62
66
|
if (toolCalls.length === 0) {
|
|
63
|
-
|
|
64
|
-
role: 'user',
|
|
65
|
-
content: `Tool call expected. Call the "report_result" tool when the task is complete.`,
|
|
66
|
-
});
|
|
67
|
+
assistantMessage.toolError = 'Error: tool call is expected in every assistant message. Call the "report_result" tool when the task is complete.';
|
|
67
68
|
continue;
|
|
68
69
|
}
|
|
69
|
-
const toolResults = [];
|
|
70
70
|
for (const toolCall of toolCalls) {
|
|
71
|
-
const { name, arguments: args
|
|
72
|
-
|
|
71
|
+
const { name, arguments: args } = toolCall;
|
|
72
|
+
debug?.('lowire:loop')('Call tool', name, JSON.stringify(args, null, 2));
|
|
73
73
|
if (name === 'report_result')
|
|
74
74
|
return args;
|
|
75
75
|
try {
|
|
76
76
|
const result = await options.callTool({
|
|
77
77
|
name,
|
|
78
|
-
arguments:
|
|
78
|
+
arguments: {
|
|
79
|
+
...args,
|
|
80
|
+
_meta: {
|
|
81
|
+
'dev.lowire/history': true,
|
|
82
|
+
'dev.lowire/state': true,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
79
85
|
});
|
|
80
86
|
const text = result.content.filter(part => part.type === 'text').map(part => part.text).join('\n');
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
toolName: name,
|
|
84
|
-
toolCallId: id,
|
|
85
|
-
result,
|
|
86
|
-
});
|
|
87
|
+
debug?.('lowire:loop')('Tool result', text, JSON.stringify(result, null, 2));
|
|
88
|
+
toolCall.result = result;
|
|
87
89
|
}
|
|
88
90
|
catch (error) {
|
|
89
91
|
const errorMessage = `Error while executing tool "${name}": ${error instanceof Error ? error.message : String(error)}\n\nPlease try to recover and complete the task.`;
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
content: [{ type: 'text', text: errorMessage }],
|
|
96
|
-
isError: true,
|
|
97
|
-
}
|
|
98
|
-
});
|
|
92
|
+
debug?.('lowire:loop')('Tool error', errorMessage, String(error));
|
|
93
|
+
toolCall.result = {
|
|
94
|
+
content: [{ type: 'text', text: errorMessage }],
|
|
95
|
+
isError: true,
|
|
96
|
+
};
|
|
99
97
|
// Skip remaining tool calls for this iteration
|
|
100
98
|
for (const remainingToolCall of toolCalls.slice(toolCalls.indexOf(toolCall) + 1)) {
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
content: [{ type: 'text', text: `This tool call is skipped due to previous error.` }],
|
|
106
|
-
isError: true,
|
|
107
|
-
}
|
|
108
|
-
});
|
|
99
|
+
remainingToolCall.result = {
|
|
100
|
+
content: [{ type: 'text', text: `This tool call is skipped due to previous error.` }],
|
|
101
|
+
isError: true,
|
|
102
|
+
};
|
|
109
103
|
}
|
|
110
104
|
break;
|
|
111
105
|
}
|
|
112
106
|
}
|
|
113
|
-
for (const toolResult of toolResults) {
|
|
114
|
-
conversation.messages.push({
|
|
115
|
-
role: 'tool_result',
|
|
116
|
-
...toolResult,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
107
|
}
|
|
108
|
+
if (options.summarize)
|
|
109
|
+
return this._summarizeConversation(task, conversation, options);
|
|
120
110
|
throw new Error('Failed to perform step, max attempts reached');
|
|
121
111
|
}
|
|
122
|
-
|
|
123
|
-
const
|
|
124
|
-
const messages = JSON.stringify(conversation.messages).length;
|
|
125
|
-
let toolsResults = 0;
|
|
126
|
-
for (const message of conversation.messages) {
|
|
127
|
-
if (message.role !== 'tool_result')
|
|
128
|
-
continue;
|
|
129
|
-
toolsResults += JSON.stringify(message.result).length;
|
|
130
|
-
}
|
|
112
|
+
_summarizeConversation(task, conversation, options) {
|
|
113
|
+
const { summary, lastMessage } = (0, summary_1.summarizeConversation)(task, conversation, options);
|
|
131
114
|
return {
|
|
132
|
-
|
|
133
|
-
messages
|
|
134
|
-
|
|
115
|
+
...conversation,
|
|
116
|
+
messages: [
|
|
117
|
+
{ role: 'user', content: summary },
|
|
118
|
+
...lastMessage ? [lastMessage] : [],
|
|
119
|
+
],
|
|
135
120
|
};
|
|
136
121
|
}
|
|
122
|
+
cache() {
|
|
123
|
+
return this._cacheOutput;
|
|
124
|
+
}
|
|
137
125
|
}
|
|
138
126
|
exports.Loop = Loop;
|
|
139
127
|
const defaultResultSchema = {
|
|
@@ -14,20 +14,21 @@
|
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
16
|
import type * as types from '../types';
|
|
17
|
-
export type
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
17
|
+
export type McpServer = {
|
|
18
|
+
command: string;
|
|
19
|
+
args?: string[];
|
|
20
|
+
cwd?: string;
|
|
21
|
+
stderr?: 'pipe' | 'inherit' | 'ignore';
|
|
22
|
+
env?: Record<string, string>;
|
|
21
23
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
}
|
|
24
|
+
type ToolFilter = (string | RegExp)[];
|
|
25
|
+
type ToolSupport = {
|
|
26
|
+
close: () => Promise<void>;
|
|
27
|
+
tools: types.Tool[];
|
|
28
|
+
callTool: types.ToolCallback;
|
|
29
|
+
};
|
|
30
|
+
export declare function createMcpTools(servers: Record<string, McpServer>, options?: {
|
|
31
|
+
rootDir?: string;
|
|
32
|
+
toolFilter?: ToolFilter;
|
|
33
|
+
}): Promise<ToolSupport>;
|
|
34
|
+
export {};
|
package/lib/mcp/index.js
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
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
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
18
|
+
if (k2 === undefined) k2 = k;
|
|
19
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
20
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
21
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
22
|
+
}
|
|
23
|
+
Object.defineProperty(o, k2, desc);
|
|
24
|
+
}) : (function(o, m, k, k2) {
|
|
25
|
+
if (k2 === undefined) k2 = k;
|
|
26
|
+
o[k2] = m[k];
|
|
27
|
+
}));
|
|
28
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
29
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
30
|
+
}) : function(o, v) {
|
|
31
|
+
o["default"] = v;
|
|
32
|
+
});
|
|
33
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
34
|
+
var ownKeys = function(o) {
|
|
35
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
36
|
+
var ar = [];
|
|
37
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
38
|
+
return ar;
|
|
39
|
+
};
|
|
40
|
+
return ownKeys(o);
|
|
41
|
+
};
|
|
42
|
+
return function (mod) {
|
|
43
|
+
if (mod && mod.__esModule) return mod;
|
|
44
|
+
var result = {};
|
|
45
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
46
|
+
__setModuleDefault(result, mod);
|
|
47
|
+
return result;
|
|
48
|
+
};
|
|
49
|
+
})();
|
|
50
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
51
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
52
|
+
};
|
|
53
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
54
|
+
exports.createMcpTools = createMcpTools;
|
|
55
|
+
const url_1 = __importDefault(require("url"));
|
|
56
|
+
async function connectToMcpServer(server, options) {
|
|
57
|
+
const { Client } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/index.js')));
|
|
58
|
+
const { StdioClientTransport } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/client/stdio.js')));
|
|
59
|
+
const { ListRootsRequestSchema } = await Promise.resolve().then(() => __importStar(require('@modelcontextprotocol/sdk/types.js')));
|
|
60
|
+
const transport = new StdioClientTransport(server);
|
|
61
|
+
const capabilities = {};
|
|
62
|
+
if (options?.rootDir)
|
|
63
|
+
capabilities['roots'] = {};
|
|
64
|
+
const client = new Client({ name: 'test', version: '1.0.0' }, { capabilities });
|
|
65
|
+
if (options?.rootDir) {
|
|
66
|
+
client.setRequestHandler(ListRootsRequestSchema, async () => {
|
|
67
|
+
return {
|
|
68
|
+
roots: [{ name: 'workspace', uri: url_1.default.pathToFileURL(options.rootDir).toString() }],
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
await client.connect(transport);
|
|
73
|
+
return client;
|
|
74
|
+
}
|
|
75
|
+
async function mcpTools(server, options) {
|
|
76
|
+
const client = await connectToMcpServer(server, options);
|
|
77
|
+
const { tools } = await client.listTools();
|
|
78
|
+
const filteredTools = options?.toolFilter ? tools.filter(tool => options.toolFilter.some(filter => typeof filter === 'string' ? tool.name === filter : filter.test(tool.name))) : tools;
|
|
79
|
+
const callTool = async (params) => {
|
|
80
|
+
return await client.callTool({ name: params.name, arguments: params.arguments });
|
|
81
|
+
};
|
|
82
|
+
return { tools: filteredTools, callTool, close: () => client.close() };
|
|
83
|
+
}
|
|
84
|
+
async function createMcpTools(servers, options) {
|
|
85
|
+
const allTools = [];
|
|
86
|
+
const callTools = new Map();
|
|
87
|
+
const closes = [];
|
|
88
|
+
for (const [name, server] of Object.entries(servers)) {
|
|
89
|
+
const { tools, callTool, close } = await mcpTools(server, options);
|
|
90
|
+
for (const tool of tools) {
|
|
91
|
+
const fullName = `${name}__${tool.name}`;
|
|
92
|
+
allTools.push({ ...tool, name: fullName });
|
|
93
|
+
callTools.set(fullName, params => callTool({ name: tool.name, arguments: params.arguments }));
|
|
94
|
+
}
|
|
95
|
+
closes.push(close);
|
|
96
|
+
}
|
|
97
|
+
return {
|
|
98
|
+
close: async () => {
|
|
99
|
+
await Promise.all(closes.map(c => c()));
|
|
100
|
+
},
|
|
101
|
+
tools: allTools,
|
|
102
|
+
callTool: async (params) => {
|
|
103
|
+
const callTool = callTools.get(params.name);
|
|
104
|
+
if (!callTool)
|
|
105
|
+
throw new Error(`Tool not found: ${params.name}`);
|
|
106
|
+
return callTool(params);
|
|
107
|
+
}
|
|
108
|
+
};
|
|
109
|
+
}
|
|
@@ -24,13 +24,13 @@ class Anthropic {
|
|
|
24
24
|
max_tokens: options.maxTokens ?? 32768,
|
|
25
25
|
temperature: options.temperature,
|
|
26
26
|
system: systemPrompt(conversation.systemPrompt),
|
|
27
|
-
messages: conversation.messages.map(
|
|
27
|
+
messages: conversation.messages.map(toAnthropicMessageParts).flat(),
|
|
28
28
|
tools: conversation.tools.map(toAnthropicTool),
|
|
29
29
|
thinking: options.reasoning ? {
|
|
30
30
|
type: 'enabled',
|
|
31
31
|
budget_tokens: options.maxTokens ? Math.round(options.maxTokens / 10) : 1024,
|
|
32
32
|
} : undefined,
|
|
33
|
-
});
|
|
33
|
+
}, options);
|
|
34
34
|
const result = toAssistantMessage(response);
|
|
35
35
|
const usage = {
|
|
36
36
|
input: response.usage.input_tokens,
|
|
@@ -40,20 +40,26 @@ class Anthropic {
|
|
|
40
40
|
}
|
|
41
41
|
}
|
|
42
42
|
exports.Anthropic = Anthropic;
|
|
43
|
-
async function create(
|
|
43
|
+
async function create(createParams, options) {
|
|
44
44
|
const headers = {
|
|
45
45
|
'Content-Type': 'application/json',
|
|
46
46
|
'x-api-key': process.env.ANTHROPIC_API_KEY,
|
|
47
47
|
'anthropic-version': '2023-06-01',
|
|
48
48
|
};
|
|
49
|
+
const debugBody = { ...createParams, tools: `${createParams.tools?.length ?? 0} tools` };
|
|
50
|
+
options.debug?.('lowire:anthropic')('Request:', JSON.stringify(debugBody, null, 2));
|
|
49
51
|
const response = await fetch(`https://api.anthropic.com/v1/messages`, {
|
|
50
52
|
method: 'POST',
|
|
51
53
|
headers,
|
|
52
|
-
body: JSON.stringify(
|
|
54
|
+
body: JSON.stringify(createParams)
|
|
53
55
|
});
|
|
54
|
-
if (!response.ok)
|
|
56
|
+
if (!response.ok) {
|
|
57
|
+
options.debug?.('lowire:anthropic')('Response:', response.status);
|
|
55
58
|
throw new Error(`API error: ${response.status} ${response.statusText} ${await response.text()}`);
|
|
56
|
-
|
|
59
|
+
}
|
|
60
|
+
const responseBody = await response.json();
|
|
61
|
+
options.debug?.('lowire:anthropic')('Response:', JSON.stringify(responseBody, null, 2));
|
|
62
|
+
return responseBody;
|
|
57
63
|
}
|
|
58
64
|
function toContentPart(block) {
|
|
59
65
|
if (block.type === 'text') {
|
|
@@ -113,6 +119,7 @@ function toAnthropicTool(tool) {
|
|
|
113
119
|
}
|
|
114
120
|
function toAnthropicAssistantMessageParam(message) {
|
|
115
121
|
const content = [];
|
|
122
|
+
const toolResults = [];
|
|
116
123
|
for (const part of message.content) {
|
|
117
124
|
if (part.type === 'text') {
|
|
118
125
|
content.push({ ...part, citations: [] });
|
|
@@ -125,6 +132,8 @@ function toAnthropicAssistantMessageParam(message) {
|
|
|
125
132
|
name: part.name,
|
|
126
133
|
input: part.arguments
|
|
127
134
|
});
|
|
135
|
+
if (part.result)
|
|
136
|
+
toolResults.push(toAnthropicToolResultMessage(part, part.result));
|
|
128
137
|
continue;
|
|
129
138
|
}
|
|
130
139
|
if (part.type === 'thinking') {
|
|
@@ -136,34 +145,41 @@ function toAnthropicAssistantMessageParam(message) {
|
|
|
136
145
|
continue;
|
|
137
146
|
}
|
|
138
147
|
}
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
148
|
+
if (message.toolError) {
|
|
149
|
+
toolResults.push({
|
|
150
|
+
role: 'user',
|
|
151
|
+
content: [{
|
|
152
|
+
type: 'text',
|
|
153
|
+
text: message.toolError,
|
|
154
|
+
}]
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
return [{
|
|
158
|
+
role: 'assistant',
|
|
159
|
+
content
|
|
160
|
+
}, ...toolResults];
|
|
143
161
|
}
|
|
144
|
-
function toAnthropicToolResultMessage(
|
|
162
|
+
function toAnthropicToolResultMessage(call, result) {
|
|
145
163
|
const toolResult = {
|
|
146
164
|
type: 'tool_result',
|
|
147
|
-
tool_use_id:
|
|
148
|
-
content:
|
|
149
|
-
is_error:
|
|
165
|
+
tool_use_id: call.id,
|
|
166
|
+
content: result.content.map(toAnthropicResultParam),
|
|
167
|
+
is_error: result.isError,
|
|
150
168
|
};
|
|
151
169
|
return {
|
|
152
170
|
role: 'user',
|
|
153
171
|
content: [toolResult]
|
|
154
172
|
};
|
|
155
173
|
}
|
|
156
|
-
function
|
|
174
|
+
function toAnthropicMessageParts(message) {
|
|
157
175
|
if (message.role === 'user') {
|
|
158
|
-
return {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
176
|
+
return [{
|
|
177
|
+
role: 'user',
|
|
178
|
+
content: message.content
|
|
179
|
+
}];
|
|
162
180
|
}
|
|
163
181
|
if (message.role === 'assistant')
|
|
164
182
|
return toAnthropicAssistantMessageParam(message);
|
|
165
|
-
if (message.role === 'tool_result')
|
|
166
|
-
return toAnthropicToolResultMessage(message);
|
|
167
183
|
throw new Error(`Unsupported message role: ${message.role}`);
|
|
168
184
|
}
|
|
169
185
|
const systemPrompt = (prompt) => `
|
|
@@ -13,21 +13,21 @@
|
|
|
13
13
|
* See the License for the specific language governing permissions and
|
|
14
14
|
* limitations under the License.
|
|
15
15
|
*/
|
|
16
|
-
import { OpenAICompletions } from './openaiCompletions';
|
|
17
|
-
import type { Endpoint } from './openaiCompletions';
|
|
18
16
|
import type * as types from '../types';
|
|
17
|
+
export declare class Github implements types.Provider {
|
|
18
|
+
readonly name: string;
|
|
19
|
+
private _apiKey;
|
|
20
|
+
private _bearer;
|
|
21
|
+
complete(conversation: types.Conversation, options: types.CompletionOptions): Promise<{
|
|
22
|
+
result: types.AssistantMessage;
|
|
23
|
+
usage: types.Usage;
|
|
24
|
+
}>;
|
|
25
|
+
}
|
|
19
26
|
export declare const kEditorHeaders: {
|
|
20
27
|
'Editor-Version': string;
|
|
21
28
|
'Editor-Plugin-Version': string;
|
|
22
29
|
'User-Agent': string;
|
|
23
30
|
Accept: string;
|
|
24
31
|
'Content-Type': string;
|
|
32
|
+
'Copilot-Vision-Request': string;
|
|
25
33
|
};
|
|
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
|
-
}
|