@looopy-ai/core 1.0.3 → 1.1.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/core/iteration.js +1 -1
- package/dist/core/tools.js +50 -48
- package/dist/observability/spans/tool.d.ts +2 -2
- package/dist/tools/client-tool-provider.d.ts +4 -4
- package/dist/tools/client-tool-provider.js +6 -7
- package/dist/tools/local-tools.d.ts +3 -3
- package/dist/tools/local-tools.js +14 -1
- package/dist/tools/mcp-tool-provider.d.ts +3 -1
- package/dist/tools/mcp-tool-provider.js +8 -3
- package/dist/types/event.d.ts +3 -0
- package/dist/types/tools.d.ts +3 -1
- package/dist/types/tools.js +1 -0
- package/package.json +1 -1
package/dist/core/iteration.js
CHANGED
|
@@ -31,7 +31,7 @@ export const runIteration = (context, config, history) => {
|
|
|
31
31
|
logger: context.logger.child({ iteration: config.iterationNumber }),
|
|
32
32
|
parentContext: iterationContext,
|
|
33
33
|
}, event)));
|
|
34
|
-
return concat(llmEvents
|
|
34
|
+
return concat(llmEvents$.pipe(filter((event) => event.kind !== 'tool-call')), toolEvents$).pipe(finishIterationSpan);
|
|
35
35
|
};
|
|
36
36
|
const prepareMessages = (context, history) => {
|
|
37
37
|
const messages = [];
|
package/dist/core/tools.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { concat, defer, of } from 'rxjs';
|
|
1
|
+
import { concat, defer, mergeMap, of } from 'rxjs';
|
|
2
2
|
import { startToolExecuteSpan } from '../observability/spans';
|
|
3
3
|
export const runToolCall = (context, toolCall) => {
|
|
4
4
|
const logger = context.logger.child({
|
|
@@ -6,54 +6,56 @@ export const runToolCall = (context, toolCall) => {
|
|
|
6
6
|
toolCallId: toolCall.toolCallId,
|
|
7
7
|
toolName: toolCall.toolName,
|
|
8
8
|
});
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
const { tapFinish } = startToolExecuteSpan(context, toolStartEvent);
|
|
19
|
-
const toolResultEvents$ = defer(async () => {
|
|
20
|
-
logger.trace('Executing tool');
|
|
21
|
-
const provider = context.toolProviders.find((p) => p.canHandle(toolCall.toolName));
|
|
22
|
-
if (!provider) {
|
|
23
|
-
logger.warn('No provider found for tool');
|
|
24
|
-
const errorMessage = `No provider found for tool: ${toolCall.toolName}`;
|
|
25
|
-
return createToolErrorEvent(context, toolCall, errorMessage);
|
|
26
|
-
}
|
|
27
|
-
try {
|
|
28
|
-
const result = await provider.execute({
|
|
29
|
-
id: toolCall.toolCallId,
|
|
30
|
-
type: 'function',
|
|
31
|
-
function: {
|
|
32
|
-
name: toolCall.toolName,
|
|
33
|
-
arguments: toolCall.arguments,
|
|
34
|
-
},
|
|
35
|
-
}, {
|
|
36
|
-
contextId: context.contextId,
|
|
37
|
-
taskId: context.taskId,
|
|
38
|
-
agentId: context.agentId,
|
|
39
|
-
parentContext: context.parentContext,
|
|
40
|
-
authContext: context.authContext,
|
|
41
|
-
});
|
|
42
|
-
logger.trace({
|
|
43
|
-
success: result.success,
|
|
44
|
-
}, 'Tool execution complete');
|
|
45
|
-
return createToolCompleteEvent(context, toolCall, result.result);
|
|
9
|
+
return defer(async () => {
|
|
10
|
+
const matchingProviders = await Promise.all(context.toolProviders.map(async (p) => ({
|
|
11
|
+
provider: p,
|
|
12
|
+
tool: await p.getTool(toolCall.toolName),
|
|
13
|
+
})));
|
|
14
|
+
const matchingProvider = matchingProviders.find((p) => p.tool !== undefined);
|
|
15
|
+
if (!matchingProvider) {
|
|
16
|
+
logger.warn('No tool provider found for tool');
|
|
17
|
+
return of(toolCall);
|
|
46
18
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
19
|
+
const { provider, tool } = matchingProvider;
|
|
20
|
+
logger.trace({ providerName: provider.name }, 'Found tool provider for tool');
|
|
21
|
+
const toolStartEvent = {
|
|
22
|
+
kind: 'tool-start',
|
|
23
|
+
contextId: context.contextId,
|
|
24
|
+
taskId: context.taskId,
|
|
25
|
+
toolCallId: toolCall.toolCallId,
|
|
26
|
+
icon: tool.icon,
|
|
27
|
+
toolName: toolCall.toolName,
|
|
28
|
+
arguments: toolCall.arguments,
|
|
29
|
+
timestamp: new Date().toISOString(),
|
|
30
|
+
};
|
|
31
|
+
const { tapFinish } = startToolExecuteSpan(context, toolCall);
|
|
32
|
+
const toolResultEvents$ = defer(async () => {
|
|
33
|
+
logger.trace({ providerName: provider.name }, 'Executing tool');
|
|
34
|
+
try {
|
|
35
|
+
const result = await provider.execute({
|
|
36
|
+
id: toolCall.toolCallId,
|
|
37
|
+
type: 'function',
|
|
38
|
+
function: {
|
|
39
|
+
name: toolCall.toolName,
|
|
40
|
+
arguments: toolCall.arguments,
|
|
41
|
+
},
|
|
42
|
+
}, context);
|
|
43
|
+
logger.trace({
|
|
44
|
+
success: result.success,
|
|
45
|
+
}, 'Tool execution complete');
|
|
46
|
+
return createToolCompleteEvent(context, toolCall, result.result);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
50
|
+
logger.error({
|
|
51
|
+
error: err.message,
|
|
52
|
+
stack: err.stack,
|
|
53
|
+
}, 'Tool execution failed');
|
|
54
|
+
return createToolErrorEvent(context, toolCall, err.message);
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
return concat(of(toolStartEvent), toolResultEvents$).pipe(tapFinish);
|
|
58
|
+
}).pipe(mergeMap((obs) => obs));
|
|
57
59
|
};
|
|
58
60
|
const createToolCompleteEvent = (context, toolCall, result) => ({
|
|
59
61
|
kind: 'tool-complete',
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { IterationContext } from '../../core/types';
|
|
2
|
-
import type {
|
|
2
|
+
import type { ToolCallEvent, ToolExecutionEvent } from '../../types/event';
|
|
3
3
|
import type { ToolCall } from '../../types/tools';
|
|
4
4
|
export interface ToolExecutionSpanParams {
|
|
5
5
|
agentId: string;
|
|
@@ -7,7 +7,7 @@ export interface ToolExecutionSpanParams {
|
|
|
7
7
|
toolCall: ToolCall;
|
|
8
8
|
parentContext?: import('@opentelemetry/api').Context;
|
|
9
9
|
}
|
|
10
|
-
export declare const startToolExecuteSpan: (context: IterationContext, toolStart:
|
|
10
|
+
export declare const startToolExecuteSpan: (context: IterationContext, toolStart: ToolCallEvent) => {
|
|
11
11
|
span: import("@opentelemetry/api").Span;
|
|
12
12
|
traceContext: import("@opentelemetry/api").Context;
|
|
13
13
|
tapFinish: import("rxjs").MonoTypeOperatorFunction<ToolExecutionEvent>;
|
|
@@ -5,18 +5,18 @@ export interface ClientToolConfig {
|
|
|
5
5
|
onInputRequired: (toolCall: ToolCall, context: ExecutionContext) => Promise<ToolResult>;
|
|
6
6
|
}
|
|
7
7
|
export declare class ClientToolProvider implements ToolProvider {
|
|
8
|
+
name: string;
|
|
8
9
|
private readonly tools;
|
|
9
10
|
private readonly toolNames;
|
|
10
11
|
private readonly onInputRequired;
|
|
11
12
|
constructor(config: ClientToolConfig);
|
|
12
13
|
getTools(): Promise<ToolDefinition[]>;
|
|
13
|
-
canHandle(toolName: string): boolean;
|
|
14
14
|
execute(toolCall: ToolCall, context: ExecutionContext): Promise<ToolResult>;
|
|
15
|
-
getTool(name: string): ToolDefinition | undefined
|
|
16
|
-
validateToolArguments(toolCall: ToolCall): {
|
|
15
|
+
getTool(name: string): Promise<ToolDefinition | undefined>;
|
|
16
|
+
validateToolArguments(toolCall: ToolCall): Promise<{
|
|
17
17
|
valid: boolean;
|
|
18
18
|
errors?: string[];
|
|
19
|
-
}
|
|
19
|
+
}>;
|
|
20
20
|
private validateRequiredParams;
|
|
21
21
|
private validateUnknownParams;
|
|
22
22
|
private validateParamTypes;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { validateToolDefinitions, } from '../types/tools';
|
|
2
2
|
export class ClientToolProvider {
|
|
3
|
+
name = 'client-tool-provider';
|
|
3
4
|
tools;
|
|
4
5
|
toolNames;
|
|
5
6
|
onInputRequired;
|
|
@@ -24,11 +25,9 @@ export class ClientToolProvider {
|
|
|
24
25
|
async getTools() {
|
|
25
26
|
return [...this.tools];
|
|
26
27
|
}
|
|
27
|
-
canHandle(toolName) {
|
|
28
|
-
return this.toolNames.has(toolName);
|
|
29
|
-
}
|
|
30
28
|
async execute(toolCall, context) {
|
|
31
|
-
|
|
29
|
+
const tool = await this.getTool(toolCall.function.name);
|
|
30
|
+
if (!tool) {
|
|
32
31
|
return {
|
|
33
32
|
toolCallId: toolCall.id,
|
|
34
33
|
toolName: toolCall.function.name,
|
|
@@ -60,11 +59,11 @@ export class ClientToolProvider {
|
|
|
60
59
|
};
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
|
-
getTool(name) {
|
|
62
|
+
async getTool(name) {
|
|
64
63
|
return this.tools.find((t) => t.name === name);
|
|
65
64
|
}
|
|
66
|
-
validateToolArguments(toolCall) {
|
|
67
|
-
const tool = this.getTool(toolCall.function.name);
|
|
65
|
+
async validateToolArguments(toolCall) {
|
|
66
|
+
const tool = await this.getTool(toolCall.function.name);
|
|
68
67
|
if (!tool) {
|
|
69
68
|
return { valid: false, errors: [`Tool ${toolCall.function.name} not found`] };
|
|
70
69
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { z } from 'zod';
|
|
2
2
|
import type { ExecutionContext } from '../types/context';
|
|
3
3
|
import type { ToolProvider } from '../types/tools';
|
|
4
|
-
type ToolHandler<TParams> = (params: TParams, context: ExecutionContext) => Promise<unknown> | unknown;
|
|
5
|
-
interface LocalToolDefinition<TSchema extends z.ZodObject> {
|
|
4
|
+
export type ToolHandler<TParams> = (params: TParams, context: ExecutionContext) => Promise<unknown> | unknown;
|
|
5
|
+
export interface LocalToolDefinition<TSchema extends z.ZodObject> {
|
|
6
6
|
name: string;
|
|
7
7
|
description: string;
|
|
8
|
+
icon?: string;
|
|
8
9
|
schema: TSchema;
|
|
9
10
|
handler: ToolHandler<z.infer<TSchema>>;
|
|
10
11
|
}
|
|
11
12
|
export declare function tool<TSchema extends z.ZodObject>(name: string, description: string, schema: TSchema, handler: ToolHandler<z.infer<TSchema>>): LocalToolDefinition<TSchema>;
|
|
12
13
|
export declare function localTools(tools: LocalToolDefinition<z.ZodObject>[]): ToolProvider;
|
|
13
|
-
export {};
|
|
@@ -19,12 +19,25 @@ export function localTools(tools) {
|
|
|
19
19
|
toolMap.set(tool.name, tool);
|
|
20
20
|
}
|
|
21
21
|
return {
|
|
22
|
+
name: 'local-tool-provider',
|
|
22
23
|
getTools: async () => tools.map((t) => ({
|
|
23
24
|
name: t.name,
|
|
24
25
|
description: t.description,
|
|
26
|
+
icon: t.icon,
|
|
25
27
|
parameters: zodToJsonSchema(t.schema),
|
|
26
28
|
})),
|
|
27
|
-
|
|
29
|
+
getTool: async (toolName) => {
|
|
30
|
+
const toolDef = toolMap.get(toolName);
|
|
31
|
+
if (!toolDef) {
|
|
32
|
+
return undefined;
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
name: toolDef.name,
|
|
36
|
+
description: toolDef.description,
|
|
37
|
+
icon: toolDef.icon,
|
|
38
|
+
parameters: zodToJsonSchema(toolDef.schema),
|
|
39
|
+
};
|
|
40
|
+
},
|
|
28
41
|
execute: async (toolCall, context) => {
|
|
29
42
|
const toolDef = toolMap.get(toolCall.function.name);
|
|
30
43
|
if (!toolDef) {
|
|
@@ -8,6 +8,7 @@ export interface MCPProviderConfig {
|
|
|
8
8
|
}
|
|
9
9
|
export declare const mcp: (config: MCPProviderConfig) => McpToolProvider;
|
|
10
10
|
export declare class McpToolProvider implements ToolProvider {
|
|
11
|
+
name: string;
|
|
11
12
|
readonly id: string;
|
|
12
13
|
private readonly client;
|
|
13
14
|
private toolCache;
|
|
@@ -15,8 +16,9 @@ export declare class McpToolProvider implements ToolProvider {
|
|
|
15
16
|
private readonly cacheTTL;
|
|
16
17
|
private ongoingRequest;
|
|
17
18
|
constructor(config: MCPProviderConfig);
|
|
19
|
+
getTool(toolName: string): Promise<ToolDefinition | undefined>;
|
|
20
|
+
executeBatch(toolCalls: ToolCall[], context: ExecutionContext): Promise<ToolResult[]>;
|
|
18
21
|
getTools(): Promise<ToolDefinition[]>;
|
|
19
|
-
canHandle(toolName: string): boolean;
|
|
20
22
|
execute(toolCall: ToolCall, context: ExecutionContext): Promise<ToolResult>;
|
|
21
23
|
private convertMCPTool;
|
|
22
24
|
}
|
|
@@ -3,6 +3,7 @@ export const mcp = (config) => {
|
|
|
3
3
|
return new McpToolProvider(config);
|
|
4
4
|
};
|
|
5
5
|
export class McpToolProvider {
|
|
6
|
+
name = 'mcp-tool-provider';
|
|
6
7
|
id;
|
|
7
8
|
client;
|
|
8
9
|
toolCache = new Map();
|
|
@@ -17,6 +18,13 @@ export class McpToolProvider {
|
|
|
17
18
|
getHeaders: config.getHeaders,
|
|
18
19
|
});
|
|
19
20
|
}
|
|
21
|
+
async getTool(toolName) {
|
|
22
|
+
const tools = await this.getTools();
|
|
23
|
+
return tools.find((tool) => tool.name === toolName);
|
|
24
|
+
}
|
|
25
|
+
async executeBatch(toolCalls, context) {
|
|
26
|
+
return Promise.all(toolCalls.map((call) => this.execute(call, context)));
|
|
27
|
+
}
|
|
20
28
|
async getTools() {
|
|
21
29
|
if (this.toolCache.size > 0 && this.cacheExpiry && Date.now() < this.cacheExpiry) {
|
|
22
30
|
return Array.from(this.toolCache.values());
|
|
@@ -40,9 +48,6 @@ export class McpToolProvider {
|
|
|
40
48
|
});
|
|
41
49
|
return this.ongoingRequest;
|
|
42
50
|
}
|
|
43
|
-
canHandle(toolName) {
|
|
44
|
-
return this.toolCache.has(toolName);
|
|
45
|
-
}
|
|
46
51
|
async execute(toolCall, context) {
|
|
47
52
|
const { name, arguments: args } = toolCall.function;
|
|
48
53
|
if (typeof args !== 'object' || args === null) {
|
package/dist/types/event.d.ts
CHANGED
|
@@ -98,6 +98,7 @@ export interface ToolStartEvent {
|
|
|
98
98
|
contextId: string;
|
|
99
99
|
taskId: string;
|
|
100
100
|
toolCallId: string;
|
|
101
|
+
icon?: string;
|
|
101
102
|
toolName: string;
|
|
102
103
|
arguments: Record<string, unknown>;
|
|
103
104
|
timestamp: string;
|
|
@@ -112,6 +113,7 @@ export interface ToolProgressEvent {
|
|
|
112
113
|
contextId: string;
|
|
113
114
|
taskId: string;
|
|
114
115
|
toolCallId: string;
|
|
116
|
+
icon?: string;
|
|
115
117
|
progress: number;
|
|
116
118
|
message?: string;
|
|
117
119
|
timestamp: string;
|
|
@@ -127,6 +129,7 @@ export interface ToolCompleteEvent {
|
|
|
127
129
|
contextId: string;
|
|
128
130
|
taskId: string;
|
|
129
131
|
toolCallId: string;
|
|
132
|
+
icon?: string;
|
|
130
133
|
toolName: string;
|
|
131
134
|
success: boolean;
|
|
132
135
|
result?: unknown;
|
package/dist/types/tools.d.ts
CHANGED
|
@@ -27,6 +27,7 @@ export type FunctionParameters = z.infer<typeof FunctionParametersSchema>;
|
|
|
27
27
|
export declare const ToolDefinitionSchema: z.ZodObject<{
|
|
28
28
|
name: z.ZodString;
|
|
29
29
|
description: z.ZodString;
|
|
30
|
+
icon: z.ZodOptional<z.ZodString>;
|
|
30
31
|
parameters: z.ZodObject<{
|
|
31
32
|
type: z.ZodLiteral<"object">;
|
|
32
33
|
properties: z.ZodRecord<z.ZodString, z.ZodType<unknown, unknown, z.core.$ZodTypeInternals<unknown, unknown>>>;
|
|
@@ -50,8 +51,9 @@ export declare const ToolCallSchema: z.ZodObject<{
|
|
|
50
51
|
}, z.core.$strip>;
|
|
51
52
|
}, z.core.$strip>;
|
|
52
53
|
export type ToolProvider = {
|
|
54
|
+
get name(): string;
|
|
55
|
+
getTool(toolName: string): Promise<ToolDefinition | undefined>;
|
|
53
56
|
getTools(): Promise<ToolDefinition[]>;
|
|
54
57
|
execute(toolCall: ToolCall, context: ExecutionContext): Promise<ToolResult>;
|
|
55
|
-
canHandle(toolName: string): boolean;
|
|
56
58
|
executeBatch?(toolCalls: ToolCall[], context: ExecutionContext): Promise<ToolResult[]>;
|
|
57
59
|
};
|
package/dist/types/tools.js
CHANGED
|
@@ -31,6 +31,7 @@ export const ToolDefinitionSchema = z.object({
|
|
|
31
31
|
.max(64)
|
|
32
32
|
.regex(/^[a-zA-Z0-9_-]+$/, 'Tool name must contain only alphanumeric characters, underscores, and hyphens'),
|
|
33
33
|
description: z.string().min(1).max(1024),
|
|
34
|
+
icon: z.string().optional(),
|
|
34
35
|
parameters: FunctionParametersSchema,
|
|
35
36
|
});
|
|
36
37
|
export function validateToolDefinitions(tools) {
|