@loopstack/claude-module 0.22.4 → 0.22.5
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/claude.module.d.ts.map +1 -1
- package/dist/claude.module.js +12 -1
- package/dist/claude.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/services/claude-client.service.d.ts +1 -1
- package/dist/services/claude-client.service.d.ts.map +1 -1
- package/dist/services/claude-client.service.js +2 -2
- package/dist/services/claude-client.service.js.map +1 -1
- package/dist/services/claude-tools-helper.service.d.ts +1 -1
- package/dist/services/claude-tools-helper.service.d.ts.map +1 -1
- package/dist/services/claude-tools-helper.service.js +4 -3
- package/dist/services/claude-tools-helper.service.js.map +1 -1
- package/dist/tools/claude-generate-text.tool.d.ts.map +1 -1
- package/dist/tools/claude-generate-text.tool.js +3 -1
- package/dist/tools/claude-generate-text.tool.js.map +1 -1
- package/dist/tools/claude-web-search.tool.d.ts +27 -0
- package/dist/tools/claude-web-search.tool.d.ts.map +1 -0
- package/dist/tools/claude-web-search.tool.js +192 -0
- package/dist/tools/claude-web-search.tool.js.map +1 -0
- package/dist/tools/delegate-tool-calls.tool.d.ts.map +1 -1
- package/dist/tools/delegate-tool-calls.tool.js +18 -2
- package/dist/tools/delegate-tool-calls.tool.js.map +1 -1
- package/dist/tools/index.d.ts +1 -0
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +1 -0
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/update-tool-result.tool.d.ts +3 -0
- package/dist/tools/update-tool-result.tool.d.ts.map +1 -1
- package/dist/tools/update-tool-result.tool.js +21 -1
- package/dist/tools/update-tool-result.tool.js.map +1 -1
- package/dist/types/claude-web-search.types.d.ts +15 -0
- package/dist/types/claude-web-search.types.d.ts.map +1 -0
- package/dist/types/claude-web-search.types.js +3 -0
- package/dist/types/claude-web-search.types.js.map +1 -0
- package/dist/types/claude.types.d.ts +8 -0
- package/dist/types/claude.types.d.ts.map +1 -1
- package/dist/types/claude.types.js.map +1 -1
- package/dist/types/index.d.ts +1 -0
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +5 -8
- package/src/claude.module.ts +0 -29
- package/src/documents/claude-message-document.ts +0 -20
- package/src/documents/claude-message-document.yaml +0 -5
- package/src/documents/index.ts +0 -1
- package/src/index.ts +0 -4
- package/src/schemas/claude-generate-tool-base.schema.ts +0 -23
- package/src/services/claude-client.service.ts +0 -34
- package/src/services/claude-messages-helper.service.ts +0 -41
- package/src/services/claude-tools-helper.service.ts +0 -36
- package/src/services/index.ts +0 -3
- package/src/tools/claude-generate-document.tool.ts +0 -29
- package/src/tools/claude-generate-object.tool.ts +0 -135
- package/src/tools/claude-generate-text.tool.ts +0 -128
- package/src/tools/delegate-tool-calls.tool.ts +0 -139
- package/src/tools/index.ts +0 -5
- package/src/tools/update-tool-result.tool.ts +0 -99
- package/src/types/claude.types.ts +0 -60
- package/src/types/index.ts +0 -1
- package/src/utils/cache.utils.ts +0 -59
|
@@ -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
|
-
}
|
package/src/tools/index.ts
DELETED
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
import { Injectable, Logger } from '@nestjs/common';
|
|
2
|
-
import { z } from 'zod';
|
|
3
|
-
import { BaseTool, DocumentClass, Tool, ToolResult, getBlockTool, getBlockTypeFromMetadata } from '@loopstack/common';
|
|
4
|
-
|
|
5
|
-
const UpdateToolResultSchema = z.object({
|
|
6
|
-
delegateResult: z.object({
|
|
7
|
-
allCompleted: z.boolean(),
|
|
8
|
-
toolResults: z.array(z.any()),
|
|
9
|
-
message: z.any(),
|
|
10
|
-
pendingCount: z.number(),
|
|
11
|
-
}),
|
|
12
|
-
completedTool: z.any(),
|
|
13
|
-
document: z
|
|
14
|
-
.custom<DocumentClass>((val) => typeof val === 'function' && getBlockTypeFromMetadata(val as object) === 'document')
|
|
15
|
-
.optional(),
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
type UpdateToolResultArgs = z.infer<typeof UpdateToolResultSchema>;
|
|
19
|
-
|
|
20
|
-
@Injectable()
|
|
21
|
-
@Tool({
|
|
22
|
-
uiConfig: {
|
|
23
|
-
description: 'Handle async tool completion callback. Updates the response document and tracks completion state.',
|
|
24
|
-
},
|
|
25
|
-
schema: UpdateToolResultSchema,
|
|
26
|
-
})
|
|
27
|
-
export class UpdateToolResult extends BaseTool {
|
|
28
|
-
private readonly logger = new Logger(UpdateToolResult.name);
|
|
29
|
-
|
|
30
|
-
async call(args: UpdateToolResultArgs): Promise<ToolResult> {
|
|
31
|
-
const { delegateResult } = args;
|
|
32
|
-
const completedToolRecord = args.completedTool as Record<string, unknown>;
|
|
33
|
-
|
|
34
|
-
// 1. Extract tool identity from subscriber metadata
|
|
35
|
-
const subscriberMetadata = completedToolRecord._subscriberMetadata as
|
|
36
|
-
| { toolUseId: string; toolName: string }
|
|
37
|
-
| undefined;
|
|
38
|
-
if (!subscriberMetadata?.toolUseId || !subscriberMetadata?.toolName) {
|
|
39
|
-
throw new Error(
|
|
40
|
-
'Callback payload is missing _subscriberMetadata with toolUseId and toolName. ' +
|
|
41
|
-
'Ensure the event subscriber was registered with metadata by delegateToolCalls.',
|
|
42
|
-
);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const { toolUseId, toolName } = subscriberMetadata;
|
|
46
|
-
|
|
47
|
-
// 2. Resolve tool and call complete()
|
|
48
|
-
const tool = getBlockTool<BaseTool>(this.ctx.parent, toolName);
|
|
49
|
-
if (!tool) {
|
|
50
|
-
throw new Error(`Tool with name ${toolName} not found.`);
|
|
51
|
-
}
|
|
52
|
-
let toolResult: ToolResult;
|
|
53
|
-
try {
|
|
54
|
-
toolResult = await tool.complete(completedToolRecord);
|
|
55
|
-
} catch (error) {
|
|
56
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
57
|
-
this.logger.error(`Tool "${toolName}" complete() failed: ${errorMessage}`);
|
|
58
|
-
toolResult = {
|
|
59
|
-
data: errorMessage,
|
|
60
|
-
error: errorMessage,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// 3. Merge into toolResults (append completed result)
|
|
65
|
-
const completedEntry = {
|
|
66
|
-
type: 'tool_result' as const,
|
|
67
|
-
tool_use_id: toolUseId,
|
|
68
|
-
content: toolResult.data ? JSON.stringify(toolResult.data, null, 2) : '',
|
|
69
|
-
is_error: !!toolResult.error,
|
|
70
|
-
};
|
|
71
|
-
const updatedResults = [...(delegateResult.toolResults as Record<string, unknown>[]), completedEntry];
|
|
72
|
-
|
|
73
|
-
// 4. Update completion state
|
|
74
|
-
const pendingCount = delegateResult.pendingCount - 1;
|
|
75
|
-
const allCompleted = pendingCount === 0;
|
|
76
|
-
|
|
77
|
-
// 5. Update response document (invalidates previous)
|
|
78
|
-
if (args.document) {
|
|
79
|
-
await this.repository.save(
|
|
80
|
-
args.document,
|
|
81
|
-
{
|
|
82
|
-
role: 'assistant',
|
|
83
|
-
content: (delegateResult.message as Record<string, unknown>).content,
|
|
84
|
-
toolResults: updatedResults,
|
|
85
|
-
},
|
|
86
|
-
{ id: (delegateResult.message as Record<string, unknown>).id as string, validate: 'skip' },
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
return {
|
|
91
|
-
data: {
|
|
92
|
-
...delegateResult,
|
|
93
|
-
toolResults: updatedResults,
|
|
94
|
-
allCompleted,
|
|
95
|
-
pendingCount,
|
|
96
|
-
},
|
|
97
|
-
};
|
|
98
|
-
}
|
|
99
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
import type { ToolCallsMap } from '@loopstack/common';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Configuration for creating a Claude client and selecting a model.
|
|
6
|
-
*/
|
|
7
|
-
export interface ClaudeModelConfig {
|
|
8
|
-
/** Model name (e.g. 'claude-sonnet-4-20250514'). Falls back to CLAUDE_MODEL env var. */
|
|
9
|
-
model?: string;
|
|
10
|
-
/** Environment variable name containing the API key. Falls back to ANTHROPIC_API_KEY. */
|
|
11
|
-
envApiKey?: string;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Options for a Claude messages API call.
|
|
16
|
-
*/
|
|
17
|
-
export interface ClaudeGenerateOptions {
|
|
18
|
-
messages: Anthropic.MessageParam[];
|
|
19
|
-
system?: string;
|
|
20
|
-
tools?: Anthropic.Tool[];
|
|
21
|
-
maxTokens?: number;
|
|
22
|
-
toolChoice?: Anthropic.MessageCreateParams['tool_choice'];
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* A tool definition in Anthropic's native format.
|
|
27
|
-
*/
|
|
28
|
-
export interface ClaudeToolDefinition {
|
|
29
|
-
name: string;
|
|
30
|
-
description: string;
|
|
31
|
-
input_schema: Anthropic.Tool['input_schema'];
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Result data from ClaudeGenerateText tool.
|
|
36
|
-
* Extends the Anthropic Message with optional tool call metadata.
|
|
37
|
-
*/
|
|
38
|
-
export interface ClaudeGenerateTextResult extends Anthropic.Message {
|
|
39
|
-
toolCalls?: ToolCallsMap;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Result data from DelegateToolCalls tool.
|
|
44
|
-
*/
|
|
45
|
-
export interface DelegateToolCallsResult {
|
|
46
|
-
allCompleted: boolean;
|
|
47
|
-
toolResults: DelegateToolResultEntry[];
|
|
48
|
-
message: { id?: string; content: Anthropic.ContentBlock[] };
|
|
49
|
-
pendingCount: number;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export interface DelegateToolResultEntry {
|
|
53
|
-
type: 'tool_result';
|
|
54
|
-
tool_use_id: string;
|
|
55
|
-
content?: string;
|
|
56
|
-
is_error?: boolean;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Re-export Anthropic namespace for consumer convenience
|
|
60
|
-
export { default as Anthropic } from '@anthropic-ai/sdk';
|
package/src/types/index.ts
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export * from './claude.types';
|
package/src/utils/cache.utils.ts
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
-
|
|
3
|
-
const CACHE_CONTROL: Anthropic.CacheControlEphemeral = { type: 'ephemeral' };
|
|
4
|
-
|
|
5
|
-
/**
|
|
6
|
-
* Returns a copy of the API call options with prompt caching breakpoints applied.
|
|
7
|
-
*
|
|
8
|
-
* Breakpoints are placed on:
|
|
9
|
-
* 1. The system prompt (stable across turns)
|
|
10
|
-
* 2. The last tool definition (stable across turns)
|
|
11
|
-
* 3. The last content block of the last message (covers growing conversation)
|
|
12
|
-
*
|
|
13
|
-
* Does not mutate the input — returns new objects with cache_control markers.
|
|
14
|
-
*/
|
|
15
|
-
export function applyCacheBreakpoints(options: {
|
|
16
|
-
system?: string | Anthropic.TextBlockParam[];
|
|
17
|
-
tools?: Anthropic.Tool[];
|
|
18
|
-
messages: Anthropic.MessageParam[];
|
|
19
|
-
}): {
|
|
20
|
-
system?: string | Anthropic.TextBlockParam[];
|
|
21
|
-
tools?: Anthropic.Tool[];
|
|
22
|
-
messages: Anthropic.MessageParam[];
|
|
23
|
-
} {
|
|
24
|
-
// 1. System prompt → convert string to block array with cache_control
|
|
25
|
-
let system: string | Anthropic.TextBlockParam[] | undefined = options.system;
|
|
26
|
-
if (system && typeof system === 'string') {
|
|
27
|
-
system = [{ type: 'text', text: system, cache_control: CACHE_CONTROL }];
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
// 2. Last tool definition — shallow-clone the array and mark the last entry
|
|
31
|
-
let tools = options.tools;
|
|
32
|
-
if (tools?.length) {
|
|
33
|
-
tools = [...tools];
|
|
34
|
-
const last = tools.length - 1;
|
|
35
|
-
tools[last] = { ...tools[last], cache_control: CACHE_CONTROL };
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// 3. Last message — clone only the last message and mark its last content block
|
|
39
|
-
let messages = options.messages;
|
|
40
|
-
if (messages.length > 0) {
|
|
41
|
-
messages = [...messages];
|
|
42
|
-
const lastIdx = messages.length - 1;
|
|
43
|
-
const lastMsg = messages[lastIdx];
|
|
44
|
-
|
|
45
|
-
if (typeof lastMsg.content === 'string') {
|
|
46
|
-
messages[lastIdx] = {
|
|
47
|
-
...lastMsg,
|
|
48
|
-
content: [{ type: 'text', text: lastMsg.content, cache_control: CACHE_CONTROL }],
|
|
49
|
-
};
|
|
50
|
-
} else if (Array.isArray(lastMsg.content) && lastMsg.content.length > 0) {
|
|
51
|
-
const blocks: Anthropic.ContentBlockParam[] = [...lastMsg.content];
|
|
52
|
-
const lastBlockIdx = blocks.length - 1;
|
|
53
|
-
blocks[lastBlockIdx] = { ...blocks[lastBlockIdx], cache_control: CACHE_CONTROL } as Anthropic.ContentBlockParam;
|
|
54
|
-
messages[lastIdx] = { ...lastMsg, content: blocks };
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return { system, tools, messages };
|
|
59
|
-
}
|