@loopstack/claude-module 0.22.4 → 0.23.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 (103) hide show
  1. package/dist/claude.module.d.ts.map +1 -1
  2. package/dist/claude.module.js +4 -12
  3. package/dist/claude.module.js.map +1 -1
  4. package/dist/documents/index.d.ts +1 -1
  5. package/dist/documents/index.d.ts.map +1 -1
  6. package/dist/documents/index.js +0 -15
  7. package/dist/documents/index.js.map +1 -1
  8. package/dist/index.d.ts +1 -0
  9. package/dist/index.d.ts.map +1 -1
  10. package/dist/index.js +1 -0
  11. package/dist/index.js.map +1 -1
  12. package/dist/services/claude-client.service.d.ts +6 -3
  13. package/dist/services/claude-client.service.d.ts.map +1 -1
  14. package/dist/services/claude-client.service.js +3 -3
  15. package/dist/services/claude-client.service.js.map +1 -1
  16. package/dist/services/claude-llm-provider.d.ts +22 -0
  17. package/dist/services/claude-llm-provider.d.ts.map +1 -0
  18. package/dist/services/claude-llm-provider.js +204 -0
  19. package/dist/services/claude-llm-provider.js.map +1 -0
  20. package/dist/services/index.d.ts +1 -2
  21. package/dist/services/index.d.ts.map +1 -1
  22. package/dist/services/index.js +1 -2
  23. package/dist/services/index.js.map +1 -1
  24. package/dist/tools/claude-web-search-server.tool.d.ts +13 -0
  25. package/dist/tools/claude-web-search-server.tool.d.ts.map +1 -0
  26. package/dist/tools/claude-web-search-server.tool.js +40 -0
  27. package/dist/tools/claude-web-search-server.tool.js.map +1 -0
  28. package/dist/tools/claude-web-search.tool.d.ts +25 -0
  29. package/dist/tools/claude-web-search.tool.d.ts.map +1 -0
  30. package/dist/tools/claude-web-search.tool.js +143 -0
  31. package/dist/tools/claude-web-search.tool.js.map +1 -0
  32. package/dist/tools/index.d.ts +2 -5
  33. package/dist/tools/index.d.ts.map +1 -1
  34. package/dist/tools/index.js +2 -5
  35. package/dist/tools/index.js.map +1 -1
  36. package/dist/types/claude-web-search.types.d.ts +15 -0
  37. package/dist/types/claude-web-search.types.d.ts.map +1 -0
  38. package/dist/types/claude-web-search.types.js +3 -0
  39. package/dist/types/claude-web-search.types.js.map +1 -0
  40. package/dist/types/claude.types.d.ts +5 -21
  41. package/dist/types/claude.types.d.ts.map +1 -1
  42. package/dist/types/claude.types.js.map +1 -1
  43. package/dist/types/index.d.ts +1 -0
  44. package/dist/types/index.d.ts.map +1 -1
  45. package/dist/types/index.js +1 -0
  46. package/dist/types/index.js.map +1 -1
  47. package/package.json +6 -8
  48. package/dist/documents/claude-message-document.d.ts +0 -16
  49. package/dist/documents/claude-message-document.d.ts.map +0 -1
  50. package/dist/documents/claude-message-document.js +0 -29
  51. package/dist/documents/claude-message-document.js.map +0 -1
  52. package/dist/documents/claude-message-document.yaml +0 -5
  53. package/dist/schemas/claude-generate-tool-base.schema.d.ts +0 -20
  54. package/dist/schemas/claude-generate-tool-base.schema.d.ts.map +0 -1
  55. package/dist/schemas/claude-generate-tool-base.schema.js +0 -24
  56. package/dist/schemas/claude-generate-tool-base.schema.js.map +0 -1
  57. package/dist/services/claude-messages-helper.service.d.ts +0 -10
  58. package/dist/services/claude-messages-helper.service.d.ts.map +0 -1
  59. package/dist/services/claude-messages-helper.service.js +0 -39
  60. package/dist/services/claude-messages-helper.service.js.map +0 -1
  61. package/dist/services/claude-tools-helper.service.d.ts +0 -6
  62. package/dist/services/claude-tools-helper.service.d.ts.map +0 -1
  63. package/dist/services/claude-tools-helper.service.js +0 -41
  64. package/dist/services/claude-tools-helper.service.js.map +0 -1
  65. package/dist/tools/claude-generate-document.tool.d.ts +0 -7
  66. package/dist/tools/claude-generate-document.tool.d.ts.map +0 -1
  67. package/dist/tools/claude-generate-document.tool.js +0 -42
  68. package/dist/tools/claude-generate-document.tool.js.map +0 -1
  69. package/dist/tools/claude-generate-object.tool.d.ts +0 -32
  70. package/dist/tools/claude-generate-object.tool.d.ts.map +0 -1
  71. package/dist/tools/claude-generate-object.tool.js +0 -121
  72. package/dist/tools/claude-generate-object.tool.js.map +0 -1
  73. package/dist/tools/claude-generate-text.tool.d.ts +0 -33
  74. package/dist/tools/claude-generate-text.tool.d.ts.map +0 -1
  75. package/dist/tools/claude-generate-text.tool.js +0 -126
  76. package/dist/tools/claude-generate-text.tool.js.map +0 -1
  77. package/dist/tools/delegate-tool-calls.tool.d.ts +0 -23
  78. package/dist/tools/delegate-tool-calls.tool.d.ts.map +0 -1
  79. package/dist/tools/delegate-tool-calls.tool.js +0 -117
  80. package/dist/tools/delegate-tool-calls.tool.js.map +0 -1
  81. package/dist/tools/update-tool-result.tool.d.ts +0 -19
  82. package/dist/tools/update-tool-result.tool.d.ts.map +0 -1
  83. package/dist/tools/update-tool-result.tool.js +0 -89
  84. package/dist/tools/update-tool-result.tool.js.map +0 -1
  85. package/src/claude.module.ts +0 -29
  86. package/src/documents/claude-message-document.ts +0 -20
  87. package/src/documents/claude-message-document.yaml +0 -5
  88. package/src/documents/index.ts +0 -1
  89. package/src/index.ts +0 -4
  90. package/src/schemas/claude-generate-tool-base.schema.ts +0 -23
  91. package/src/services/claude-client.service.ts +0 -34
  92. package/src/services/claude-messages-helper.service.ts +0 -41
  93. package/src/services/claude-tools-helper.service.ts +0 -36
  94. package/src/services/index.ts +0 -3
  95. package/src/tools/claude-generate-document.tool.ts +0 -29
  96. package/src/tools/claude-generate-object.tool.ts +0 -135
  97. package/src/tools/claude-generate-text.tool.ts +0 -128
  98. package/src/tools/delegate-tool-calls.tool.ts +0 -139
  99. package/src/tools/index.ts +0 -5
  100. package/src/tools/update-tool-result.tool.ts +0 -99
  101. package/src/types/claude.types.ts +0 -60
  102. package/src/types/index.ts +0 -1
  103. package/src/utils/cache.utils.ts +0 -59
@@ -1,20 +0,0 @@
1
- import { z } from 'zod';
2
- import { Document } from '@loopstack/common';
3
-
4
- export const ClaudeMessageDocumentSchema = z.object({
5
- role: z.enum(['user', 'assistant']),
6
- content: z.union([z.string(), z.array(z.any())]),
7
- toolResults: z.array(z.any()).optional(),
8
- });
9
-
10
- export type ClaudeMessageDocumentContentType = z.infer<typeof ClaudeMessageDocumentSchema>;
11
-
12
- @Document({
13
- schema: ClaudeMessageDocumentSchema,
14
- uiConfig: __dirname + '/claude-message-document.yaml',
15
- })
16
- export class ClaudeMessageDocument {
17
- role: 'user' | 'assistant';
18
- content: string | any[];
19
- toolResults?: any[];
20
- }
@@ -1,5 +0,0 @@
1
- tags:
2
- - message
3
- ui:
4
- widgets:
5
- - widget: claude-message
@@ -1 +0,0 @@
1
- export * from './claude-message-document';
package/src/index.ts DELETED
@@ -1,4 +0,0 @@
1
- export * from './claude.module';
2
- export * from './tools';
3
- export * from './documents';
4
- export * from './types';
@@ -1,23 +0,0 @@
1
- import { z } from 'zod';
2
-
3
- export const ClaudeGenerateToolBaseSchema = z.object({
4
- claude: z
5
- .object({
6
- model: z.string().optional(),
7
- envApiKey: z.string().optional(),
8
- maxTokens: z.number().optional(),
9
- cache: z.boolean().optional(),
10
- })
11
- .optional(),
12
- messages: z
13
- .array(
14
- z.object({
15
- role: z.enum(['user', 'assistant']),
16
- content: z.union([z.string(), z.array(z.any())]),
17
- }),
18
- )
19
- .optional(),
20
- system: z.string().optional(),
21
- prompt: z.string().optional(),
22
- messagesSearchTag: z.string().optional(),
23
- });
@@ -1,34 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Injectable } from '@nestjs/common';
3
- import { ClaudeModelConfig } from '../types';
4
-
5
- @Injectable()
6
- export class ClaudeClientService {
7
- private getApiKey(envApiKey?: string): string {
8
- const envVar = envApiKey ?? 'ANTHROPIC_API_KEY';
9
- const apiKey = process.env[envVar];
10
-
11
- if (!apiKey) {
12
- throw new Error(`No API key found! Please make sure to provide "${envVar}" in your .env file.`);
13
- }
14
-
15
- return apiKey;
16
- }
17
-
18
- getClient(config?: ClaudeModelConfig): Anthropic {
19
- const apiKey = this.getApiKey(config?.envApiKey);
20
- return new Anthropic({ apiKey });
21
- }
22
-
23
- getModel(config?: ClaudeModelConfig): string {
24
- const model = config?.model ?? process.env['CLAUDE_MODEL'];
25
-
26
- if (!model) {
27
- throw new Error(
28
- 'No model defined. Please provide CLAUDE_MODEL env var or set the claude.model parameter in the tool args.',
29
- );
30
- }
31
-
32
- return model;
33
- }
34
- }
@@ -1,41 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Injectable } from '@nestjs/common';
3
- import { DocumentEntity } from '@loopstack/common';
4
-
5
- @Injectable()
6
- export class ClaudeMessagesHelperService {
7
- private searchMessages(documents: DocumentEntity[], tag: string): Anthropic.MessageParam[] {
8
- return documents
9
- .filter((document) => !document.isInvalidated && document.tags?.includes(tag))
10
- .sort((a, b) => a.index - b.index)
11
- .flatMap((document) => {
12
- const content = document.content as {
13
- role: 'user' | 'assistant';
14
- content: string | Anthropic.ContentBlockParam[];
15
- toolResults?: Anthropic.ToolResultBlockParam[];
16
- };
17
-
18
- // Combined document: expand into assistant message + user tool_result message
19
- if (content.toolResults?.length) {
20
- return [
21
- { role: content.role, content: content.content } as Anthropic.MessageParam,
22
- { role: 'user' as const, content: content.toolResults } as Anthropic.MessageParam,
23
- ];
24
- }
25
-
26
- return [{ role: content.role, content: content.content } as Anthropic.MessageParam];
27
- });
28
- }
29
-
30
- getMessages(
31
- documents: DocumentEntity[],
32
- args: { messages?: Anthropic.MessageParam[]; messagesSearchTag?: string },
33
- ): Anthropic.MessageParam[] {
34
- let messages: Anthropic.MessageParam[] | undefined = args.messages;
35
- if (!messages?.length) {
36
- messages = this.searchMessages(documents, args.messagesSearchTag ?? 'message');
37
- }
38
-
39
- return messages;
40
- }
41
- }
@@ -1,36 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Injectable } from '@nestjs/common';
3
- import { toJSONSchema } from 'zod';
4
- import { ToolInterface, WorkflowInterface, getBlockArgsSchema, getBlockConfig } from '@loopstack/common';
5
- import { getBlockTool } from '@loopstack/common';
6
- import { WorkflowType } from '@loopstack/contracts/dist/types';
7
-
8
- @Injectable()
9
- export class ClaudeToolsHelperService {
10
- getTools(tools: string[], parent: WorkflowInterface): Anthropic.Tool[] | undefined {
11
- const toolDefinitions: Anthropic.Tool[] = [];
12
-
13
- for (const toolName of tools) {
14
- const tool = getBlockTool<ToolInterface>(parent, toolName);
15
- if (!tool) {
16
- throw new Error(`Tool with name ${toolName} not available in Workflow context.`);
17
- }
18
-
19
- const inputSchema = getBlockArgsSchema(tool);
20
- const config = getBlockConfig<WorkflowType>(tool);
21
- if (!config) {
22
- throw new Error(`Block ${tool.constructor.name} is missing @BlockConfig decorator`);
23
- }
24
-
25
- const jsonSchema = inputSchema ? toJSONSchema(inputSchema) : { type: 'object' };
26
-
27
- toolDefinitions.push({
28
- name: toolName,
29
- description: config.description ?? '',
30
- input_schema: jsonSchema as Anthropic.Tool['input_schema'],
31
- });
32
- }
33
-
34
- return toolDefinitions.length ? toolDefinitions : undefined;
35
- }
36
- }
@@ -1,3 +0,0 @@
1
- export * from './claude-client.service';
2
- export * from './claude-messages-helper.service';
3
- export * from './claude-tools-helper.service';
@@ -1,29 +0,0 @@
1
- import { BaseTool, InjectTool, Tool, ToolResult } from '@loopstack/common';
2
- import {
3
- ClaudeGenerateObject,
4
- ClaudeGenerateObjectArgsType,
5
- ClaudeGenerateObjectSchema,
6
- } from './claude-generate-object.tool';
7
-
8
- @Tool({
9
- uiConfig: {
10
- description: 'Generates a structured object using the Anthropic Claude API and creates it as a document',
11
- },
12
- schema: ClaudeGenerateObjectSchema,
13
- })
14
- export class ClaudeGenerateDocument extends BaseTool {
15
- @InjectTool() private claudeGenerateObject!: ClaudeGenerateObject;
16
-
17
- async call(args: ClaudeGenerateObjectArgsType): Promise<ToolResult> {
18
- const generateResult = await this.claudeGenerateObject.call(args);
19
- const entity = await this.repository.save(args.response.document, generateResult.data as Record<string, unknown>, {
20
- id: args.response.id,
21
- validate: 'skip',
22
- });
23
-
24
- return {
25
- data: entity,
26
- metadata: generateResult.metadata,
27
- };
28
- }
29
- }
@@ -1,135 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Inject } from '@nestjs/common';
3
- import { toJSONSchema, z } from 'zod';
4
- import {
5
- BaseTool,
6
- DocumentClass,
7
- Tool,
8
- ToolResult,
9
- getBlockTypeFromMetadata,
10
- getDocumentSchema,
11
- } from '@loopstack/common';
12
- import { ClaudeGenerateToolBaseSchema } from '../schemas/claude-generate-tool-base.schema';
13
- import { ClaudeClientService } from '../services';
14
- import { ClaudeMessagesHelperService } from '../services';
15
- import { applyCacheBreakpoints } from '../utils/cache.utils';
16
-
17
- export const ClaudeGenerateObjectSchema = ClaudeGenerateToolBaseSchema.extend({
18
- response: z.object({
19
- id: z.string().optional(),
20
- document: z.custom<DocumentClass>(
21
- (val) => typeof val === 'function' && getBlockTypeFromMetadata(val as object) === 'document',
22
- ),
23
- }),
24
- }).strict();
25
-
26
- export type ClaudeGenerateObjectArgsType = z.infer<typeof ClaudeGenerateObjectSchema>;
27
-
28
- const STRUCTURED_OUTPUT_TOOL_NAME = 'structured_output';
29
-
30
- @Tool({
31
- uiConfig: {
32
- description: 'Generates a structured object using the Anthropic Claude API',
33
- },
34
- schema: ClaudeGenerateObjectSchema,
35
- })
36
- export class ClaudeGenerateObject extends BaseTool {
37
- @Inject()
38
- private readonly claudeClientService: ClaudeClientService;
39
- @Inject()
40
- private readonly claudeMessagesHelperService: ClaudeMessagesHelperService;
41
-
42
- async call(args: ClaudeGenerateObjectArgsType): Promise<ToolResult> {
43
- const client = this.claudeClientService.getClient(args.claude);
44
- const model = this.claudeClientService.getModel(args.claude);
45
-
46
- const messages: Anthropic.MessageParam[] = [];
47
-
48
- if (args.prompt) {
49
- messages.push({ role: 'user', content: args.prompt });
50
- } else {
51
- const resolved = this.claudeMessagesHelperService.getMessages(this.ctx.runtime.documents, {
52
- messages: args.messages as Anthropic.MessageParam[],
53
- messagesSearchTag: args.messagesSearchTag,
54
- });
55
- messages.push(...resolved);
56
- }
57
-
58
- const responseSchema = getDocumentSchema(args.response.document);
59
- if (!responseSchema) {
60
- throw new Error('Claude object generation source document must have a schema.');
61
- }
62
-
63
- const jsonSchema = toJSONSchema(responseSchema);
64
-
65
- const response = await this.handleGenerateObject(client, {
66
- model,
67
- messages,
68
- system: args.system,
69
- maxTokens: args.claude?.maxTokens,
70
- inputSchema: jsonSchema as Anthropic.Tool['input_schema'],
71
- cache: args.claude?.cache,
72
- });
73
-
74
- return {
75
- data: response.data,
76
- metadata: {
77
- usage: {
78
- inputTokens: response.usage.input_tokens,
79
- outputTokens: response.usage.output_tokens,
80
- cacheCreationInputTokens: response.usage.cache_creation_input_tokens ?? 0,
81
- cacheReadInputTokens: response.usage.cache_read_input_tokens ?? 0,
82
- },
83
- },
84
- };
85
- }
86
-
87
- private async handleGenerateObject(
88
- client: Anthropic,
89
- options: {
90
- model: string;
91
- messages: Anthropic.MessageParam[];
92
- system?: string;
93
- maxTokens?: number;
94
- inputSchema: Anthropic.Tool['input_schema'];
95
- cache?: boolean;
96
- },
97
- ): Promise<{ data: unknown; usage: Anthropic.Usage }> {
98
- const baseTool: Anthropic.Tool = {
99
- name: STRUCTURED_OUTPUT_TOOL_NAME,
100
- description: 'Return the structured output matching the schema.',
101
- input_schema: options.inputSchema,
102
- };
103
-
104
- const { system, tools, messages } = options.cache
105
- ? applyCacheBreakpoints({ system: options.system, tools: [baseTool], messages: options.messages })
106
- : { system: options.system, tools: [baseTool], messages: options.messages };
107
-
108
- const startTime = performance.now();
109
- try {
110
- const response = await client.messages.create({
111
- model: options.model,
112
- messages,
113
- max_tokens: options.maxTokens ?? 4096,
114
- ...(system ? { system } : {}),
115
- tools: tools!,
116
- tool_choice: { type: 'tool', name: STRUCTURED_OUTPUT_TOOL_NAME },
117
- });
118
-
119
- const toolUseBlock = response.content.find((block): block is Anthropic.ToolUseBlock => block.type === 'tool_use');
120
-
121
- if (!toolUseBlock) {
122
- throw new Error('Claude did not return a tool_use block for structured output.');
123
- }
124
-
125
- return {
126
- data: toolUseBlock.input,
127
- usage: response.usage,
128
- };
129
- } catch (error) {
130
- const errorResponseTime = performance.now() - startTime;
131
- console.error(`Request failed after ${errorResponseTime}ms:`, error);
132
- throw error;
133
- }
134
- }
135
- }
@@ -1,128 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Inject } from '@nestjs/common';
3
- import { z } from 'zod';
4
- import { BaseTool, Tool, ToolCallEntry, ToolCallsMap, ToolResult } from '@loopstack/common';
5
- import { ClaudeGenerateToolBaseSchema } from '../schemas/claude-generate-tool-base.schema';
6
- import { ClaudeClientService } from '../services';
7
- import { ClaudeMessagesHelperService } from '../services';
8
- import { ClaudeToolsHelperService } from '../services';
9
- import { ClaudeGenerateTextResult } from '../types';
10
- import { applyCacheBreakpoints } from '../utils/cache.utils';
11
-
12
- export const ClaudeGenerateTextSchema = ClaudeGenerateToolBaseSchema.extend({
13
- tools: z.array(z.string()).optional(),
14
- }).strict();
15
-
16
- type ClaudeGenerateTextArgsType = z.infer<typeof ClaudeGenerateTextSchema>;
17
-
18
- @Tool({
19
- uiConfig: {
20
- description: 'Generates text using the Anthropic Claude API',
21
- },
22
- schema: ClaudeGenerateTextSchema,
23
- })
24
- export class ClaudeGenerateText extends BaseTool {
25
- @Inject()
26
- private readonly claudeClientService: ClaudeClientService;
27
- @Inject()
28
- private readonly claudeMessagesHelperService: ClaudeMessagesHelperService;
29
- @Inject()
30
- private readonly claudeToolsHelperService: ClaudeToolsHelperService;
31
-
32
- async call(args: ClaudeGenerateTextArgsType): Promise<ToolResult<ClaudeGenerateTextResult>> {
33
- const client = this.claudeClientService.getClient(args.claude);
34
- const model = this.claudeClientService.getModel(args.claude);
35
-
36
- const messages: Anthropic.MessageParam[] = [];
37
-
38
- if (args.prompt) {
39
- messages.push({ role: 'user', content: args.prompt });
40
- } else {
41
- const resolved = this.claudeMessagesHelperService.getMessages(this.ctx.runtime.documents, {
42
- messages: args.messages as Anthropic.MessageParam[],
43
- messagesSearchTag: args.messagesSearchTag,
44
- });
45
- messages.push(...resolved);
46
- }
47
-
48
- const tools = args.tools ? this.claudeToolsHelperService.getTools(args.tools, this.ctx.parent) : undefined;
49
-
50
- const response = await this.handleGenerateText(client, {
51
- model,
52
- messages,
53
- system: args.system,
54
- maxTokens: args.claude?.maxTokens,
55
- tools,
56
- cache: args.claude?.cache,
57
- });
58
-
59
- const toolCalls = this.extractToolCalls(response);
60
-
61
- return {
62
- data: {
63
- ...response,
64
- ...(toolCalls ? { toolCalls } : {}),
65
- },
66
- metadata: {
67
- usage: {
68
- inputTokens: response.usage.input_tokens,
69
- outputTokens: response.usage.output_tokens,
70
- cacheCreationInputTokens: response.usage.cache_creation_input_tokens ?? 0,
71
- cacheReadInputTokens: response.usage.cache_read_input_tokens ?? 0,
72
- },
73
- },
74
- };
75
- }
76
-
77
- private extractToolCalls(response: Anthropic.Message): ToolCallsMap | null {
78
- if (response.stop_reason !== 'tool_use') {
79
- return null;
80
- }
81
-
82
- const toolCalls: ToolCallsMap = {};
83
- for (const block of response.content) {
84
- if (block.type === 'tool_use') {
85
- toolCalls[block.name] = {
86
- id: block.id,
87
- name: block.name,
88
- input: block.input,
89
- } satisfies ToolCallEntry;
90
- }
91
- }
92
-
93
- return Object.keys(toolCalls).length > 0 ? toolCalls : null;
94
- }
95
-
96
- private async handleGenerateText(
97
- client: Anthropic,
98
- options: {
99
- model: string;
100
- messages: Anthropic.MessageParam[];
101
- system?: string;
102
- maxTokens?: number;
103
- tools?: Anthropic.Tool[];
104
- cache?: boolean;
105
- },
106
- ): Promise<Anthropic.Message> {
107
- const { system, tools, messages } = options.cache
108
- ? applyCacheBreakpoints({ system: options.system, tools: options.tools, messages: options.messages })
109
- : { system: options.system, tools: options.tools, messages: options.messages };
110
-
111
- const startTime = performance.now();
112
- try {
113
- const stream = client.messages.stream({
114
- model: options.model,
115
- messages,
116
- max_tokens: options.maxTokens ?? 4096,
117
- ...(system ? { system } : {}),
118
- ...(tools ? { tools } : {}),
119
- });
120
-
121
- return await stream.finalMessage();
122
- } catch (error) {
123
- const errorResponseTime = performance.now() - startTime;
124
- console.error(`Request failed after ${errorResponseTime}ms:`, error);
125
- throw error;
126
- }
127
- }
128
- }
@@ -1,139 +0,0 @@
1
- import Anthropic from '@anthropic-ai/sdk';
2
- import { Injectable, Logger } from '@nestjs/common';
3
- import { z } from 'zod';
4
- import {
5
- BaseTool,
6
- DocumentClass,
7
- Tool,
8
- ToolCallOptions,
9
- ToolResult,
10
- getBlockTool,
11
- getBlockTypeFromMetadata,
12
- } from '@loopstack/common';
13
- import type { DelegateToolCallsResult, DelegateToolResultEntry } from '../types';
14
-
15
- const DelegateToolCallsSchema = z.object({
16
- message: z.object({
17
- id: z.string().optional(),
18
- content: z.array(z.any()),
19
- }),
20
- document: z
21
- .custom<DocumentClass>((val) => typeof val === 'function' && getBlockTypeFromMetadata(val as object) === 'document')
22
- .optional(),
23
- skipResponseMessage: z.boolean().optional(),
24
- callback: z
25
- .object({
26
- transition: z.string(),
27
- })
28
- .optional(),
29
- });
30
-
31
- type DelegateToolCallsArgs = z.infer<typeof DelegateToolCallsSchema>;
32
-
33
- @Injectable()
34
- @Tool({
35
- uiConfig: {
36
- description: 'Delegate tool calls from an LLM response. Handles both sync tools and async task tools.',
37
- },
38
- schema: DelegateToolCallsSchema,
39
- })
40
- export class DelegateToolCalls extends BaseTool {
41
- private readonly logger = new Logger(DelegateToolCalls.name);
42
-
43
- async call(args: DelegateToolCallsArgs): Promise<ToolResult<DelegateToolCallsResult>> {
44
- const contentBlocks = args.message.content as Anthropic.ContentBlock[];
45
- const toolUseBlocks = contentBlocks.filter((b): b is Anthropic.ToolUseBlock => b.type === 'tool_use');
46
-
47
- if (toolUseBlocks.length === 0) {
48
- return {
49
- data: {
50
- allCompleted: true,
51
- toolResults: [],
52
- message: args.message as DelegateToolCallsResult['message'],
53
- pendingCount: 0,
54
- },
55
- };
56
- }
57
-
58
- // 1. Execute ALL tools in parallel, passing callback via options (not args)
59
- const results = await Promise.all(
60
- toolUseBlocks.map((block) => {
61
- const toolCallback = args.callback
62
- ? {
63
- transition: args.callback.transition,
64
- metadata: { toolUseId: block.id, toolName: block.name },
65
- }
66
- : undefined;
67
- return this.executeTool(block, toolCallback ? { callback: toolCallback } : undefined);
68
- }),
69
- );
70
-
71
- // 2. Build toolResults array — async tools signal via result.pending
72
- let pendingCount = 0;
73
- const toolResults: DelegateToolResultEntry[] = [];
74
-
75
- for (let i = 0; i < toolUseBlocks.length; i++) {
76
- const block = toolUseBlocks[i];
77
- const result = results[i];
78
-
79
- if (result.pending) {
80
- pendingCount++;
81
- } else {
82
- toolResults.push({
83
- type: 'tool_result',
84
- tool_use_id: block.id,
85
- content: result.data ? JSON.stringify(result.data, null, 2) : '',
86
- is_error: !!result.error,
87
- });
88
- }
89
- }
90
-
91
- // 3. Create response document (tool call message)
92
- if (args.document && !args.skipResponseMessage) {
93
- await this.createResponseDocument(args.document, args, toolResults);
94
- }
95
-
96
- return {
97
- data: {
98
- allCompleted: pendingCount === 0,
99
- toolResults,
100
- message: args.message as DelegateToolCallsResult['message'],
101
- pendingCount,
102
- },
103
- };
104
- }
105
-
106
- private async executeTool(block: Anthropic.ToolUseBlock, options?: ToolCallOptions): Promise<ToolResult> {
107
- try {
108
- const tool = getBlockTool<BaseTool>(this.ctx.parent, block.name);
109
- if (!tool) {
110
- throw new Error(`Tool with name ${block.name} not found.`);
111
- }
112
-
113
- return await tool.call(block.input as Record<string, unknown>, options);
114
- } catch (error) {
115
- const errorMessage = error instanceof Error ? error.message : String(error);
116
- this.logger.error(`Tool "${block.name}" failed: ${errorMessage}`);
117
- return {
118
- data: errorMessage,
119
- error: errorMessage,
120
- };
121
- }
122
- }
123
-
124
- private async createResponseDocument(
125
- document: DocumentClass,
126
- args: DelegateToolCallsArgs,
127
- toolResults: DelegateToolResultEntry[],
128
- ): Promise<void> {
129
- await this.repository.save(
130
- document,
131
- {
132
- role: 'assistant',
133
- content: args.message.content,
134
- toolResults,
135
- },
136
- { id: args.message.id, validate: 'skip' },
137
- );
138
- }
139
- }
@@ -1,5 +0,0 @@
1
- export * from './claude-generate-text.tool';
2
- export * from './claude-generate-object.tool';
3
- export * from './claude-generate-document.tool';
4
- export * from './delegate-tool-calls.tool';
5
- export * from './update-tool-result.tool';