@noetaris/harness-anthropic 0.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/README.md +62 -0
- package/dist/index.d.ts +78 -0
- package/dist/index.js +172 -0
- package/dist/index.js.map +1 -0
- package/package.json +56 -0
package/README.md
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# @noetaris/harness-anthropic
|
|
2
|
+
|
|
3
|
+
Anthropic Claude adapter for [@noetaris/harness](../core).
|
|
4
|
+
|
|
5
|
+
> **Status:** not yet released. Implementation tracked in F20.
|
|
6
|
+
|
|
7
|
+
## Overview
|
|
8
|
+
|
|
9
|
+
`@noetaris/harness-anthropic` provides a `Claude` class that implements the `LLM` and `ObserverAware` interfaces from `@noetaris/harness`. It handles translation between the harness message format and the Anthropic SDK format, and emits telemetry events (token usage, model ID) through an attached `Observer`.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
```sh
|
|
14
|
+
pnpm add @noetaris/harness-anthropic
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies:
|
|
18
|
+
|
|
19
|
+
```sh
|
|
20
|
+
pnpm add @noetaris/harness @noetaris/harness-types
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
Requires Node.js ≥ 22.
|
|
24
|
+
|
|
25
|
+
## Usage
|
|
26
|
+
|
|
27
|
+
```ts
|
|
28
|
+
import { Claude } from '@noetaris/harness-anthropic'
|
|
29
|
+
|
|
30
|
+
const llm = new Claude({ apiKey: process.env.ANTHROPIC_API_KEY })
|
|
31
|
+
|
|
32
|
+
// Wire into a harness provider slot
|
|
33
|
+
h.provide('model', runtime())
|
|
34
|
+
|
|
35
|
+
const agent = createAgent(h, { prompts: { system: '...' } })
|
|
36
|
+
const run = agent.run(initialState, { model: llm })
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
## API
|
|
40
|
+
|
|
41
|
+
### `Claude`
|
|
42
|
+
|
|
43
|
+
Implements `LLM` and `ObserverAware`.
|
|
44
|
+
|
|
45
|
+
- **`invoke(messages, options?)`** — translates harness `Message[]` and `Tool[]` to Anthropic SDK format, calls `client.messages.create()`, and maps the response back to `LLMResponse`.
|
|
46
|
+
- **`bindObserver(observer)`** — attaches an `Observer`; after each `invoke`, emits an `"llm.response"` event with `{ tokens: { input, output }, modelId }`.
|
|
47
|
+
|
|
48
|
+
### `MockClaude`
|
|
49
|
+
|
|
50
|
+
A deterministic test double for use in tests and demos without a real API key.
|
|
51
|
+
|
|
52
|
+
## Related Packages
|
|
53
|
+
|
|
54
|
+
- [`@noetaris/harness`](https://github.com/noetaris-lab/harness) — core execution engine
|
|
55
|
+
- [`@noetaris/harness-types`](https://github.com/noetaris-lab/harness-types) — shared LLM type contract
|
|
56
|
+
- [`@noetaris/harness-openai`](https://github.com/noetaris-lab/harness-openai) — OpenAI adapter
|
|
57
|
+
- [`@noetaris/harness-google`](https://github.com/noetaris-lab/harness-google) — Google Gemini adapter
|
|
58
|
+
- [`@noetaris/harness-otel`](https://github.com/noetaris-lab/harness-otel) — OpenTelemetry observer bridge
|
|
59
|
+
|
|
60
|
+
## License
|
|
61
|
+
|
|
62
|
+
MIT
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
import { LLM, Message, Tool, LLMResponse } from '@noetaris/harness-types';
|
|
2
|
+
import { ObserverAware, Observer, StepContext } from '@noetaris/harness';
|
|
3
|
+
|
|
4
|
+
/** Options for {@link Claude}. */
|
|
5
|
+
interface ClaudeOptions {
|
|
6
|
+
/** Anthropic API key. Defaults to the `ANTHROPIC_API_KEY` environment variable. */
|
|
7
|
+
apiKey?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* {@link LLM} adapter for the Anthropic Messages API.
|
|
11
|
+
*
|
|
12
|
+
* Implements {@link ObserverAware} — when an observer is bound the adapter
|
|
13
|
+
* emits an `'llm.response'` event carrying an `LLMUsageEvent` payload after
|
|
14
|
+
* each successful invocation.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* ```ts
|
|
18
|
+
* const llm = new Claude('claude-3-5-haiku-20241022')
|
|
19
|
+
* const response = await llm.invoke(messages)
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
declare class Claude implements LLM, ObserverAware {
|
|
23
|
+
private readonly client;
|
|
24
|
+
private readonly model;
|
|
25
|
+
private observer;
|
|
26
|
+
private stepContext;
|
|
27
|
+
/**
|
|
28
|
+
* @param model - Anthropic model ID, e.g. `'claude-3-5-haiku-20241022'`.
|
|
29
|
+
* @param options - Optional API key override.
|
|
30
|
+
*/
|
|
31
|
+
constructor(model: string, options?: ClaudeOptions);
|
|
32
|
+
bindObserver(observer: Observer): void;
|
|
33
|
+
setStepContext(ctx: StepContext): void;
|
|
34
|
+
invoke(messages: Message[], options?: {
|
|
35
|
+
tools?: Tool[];
|
|
36
|
+
}): Promise<LLMResponse>;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/** Thrown by {@link MockClaude} when `invoke` is called with no responses queued. */
|
|
40
|
+
declare class MockClaudeEmptyQueueError extends Error {
|
|
41
|
+
constructor();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* In-memory test double for {@link Claude}.
|
|
45
|
+
*
|
|
46
|
+
* Pre-load one or more {@link LLMResponse} objects; each `invoke()` call
|
|
47
|
+
* dequeues the next response. The last response is **sticky** — once only
|
|
48
|
+
* one remains it repeats indefinitely rather than throwing.
|
|
49
|
+
*
|
|
50
|
+
* `lastMessages` is populated after each `invoke()` call, allowing assertions
|
|
51
|
+
* on the conversation history passed to the adapter.
|
|
52
|
+
*
|
|
53
|
+
* @example
|
|
54
|
+
* ```ts
|
|
55
|
+
* const llm = new MockClaude({ text: 'hello', toolCalls: [], stopReason: 'end' })
|
|
56
|
+
* ```
|
|
57
|
+
*/
|
|
58
|
+
declare class MockClaude implements LLM, ObserverAware {
|
|
59
|
+
/** The message list from the most recent `invoke()` call. */
|
|
60
|
+
lastMessages: Message[];
|
|
61
|
+
private queue;
|
|
62
|
+
private observer;
|
|
63
|
+
private stepContext;
|
|
64
|
+
/**
|
|
65
|
+
* @param responses - One or more responses to queue up front. May also be
|
|
66
|
+
* added later via {@link enqueue}.
|
|
67
|
+
*/
|
|
68
|
+
constructor(responses?: LLMResponse | LLMResponse[]);
|
|
69
|
+
/** Add one or more responses to the end of the queue. */
|
|
70
|
+
enqueue(response: LLMResponse | LLMResponse[]): void;
|
|
71
|
+
bindObserver(observer: Observer): void;
|
|
72
|
+
setStepContext(ctx: StepContext): void;
|
|
73
|
+
invoke(messages: Message[], options?: {
|
|
74
|
+
tools?: Tool[];
|
|
75
|
+
}): Promise<LLMResponse>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export { Claude, type ClaudeOptions, MockClaude, MockClaudeEmptyQueueError };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
// src/claude.ts
|
|
2
|
+
import Anthropic from "@anthropic-ai/sdk";
|
|
3
|
+
function translateMessages(messages) {
|
|
4
|
+
const result = [];
|
|
5
|
+
for (const msg of messages) {
|
|
6
|
+
if (msg.role === "user") {
|
|
7
|
+
result.push({ role: "user", content: msg.content });
|
|
8
|
+
} else if (msg.role === "assistant") {
|
|
9
|
+
if (msg.toolCalls && msg.toolCalls.length > 0) {
|
|
10
|
+
const blocks = [];
|
|
11
|
+
if (msg.content) {
|
|
12
|
+
blocks.push({ type: "text", text: msg.content });
|
|
13
|
+
}
|
|
14
|
+
for (const tc of msg.toolCalls) {
|
|
15
|
+
blocks.push({ type: "tool_use", id: tc.id, name: tc.name, input: tc.input });
|
|
16
|
+
}
|
|
17
|
+
result.push({ role: "assistant", content: blocks });
|
|
18
|
+
} else {
|
|
19
|
+
result.push({ role: "assistant", content: msg.content ?? "" });
|
|
20
|
+
}
|
|
21
|
+
} else if (msg.role === "tool") {
|
|
22
|
+
const toolResultBlock = {
|
|
23
|
+
type: "tool_result",
|
|
24
|
+
tool_use_id: msg.toolCallId,
|
|
25
|
+
content: msg.content
|
|
26
|
+
};
|
|
27
|
+
const last = result[result.length - 1];
|
|
28
|
+
if (last !== void 0 && last.role === "user") {
|
|
29
|
+
if (typeof last.content === "string") {
|
|
30
|
+
last.content = [
|
|
31
|
+
{ type: "text", text: last.content },
|
|
32
|
+
toolResultBlock
|
|
33
|
+
];
|
|
34
|
+
} else {
|
|
35
|
+
last.content.push(toolResultBlock);
|
|
36
|
+
}
|
|
37
|
+
} else {
|
|
38
|
+
result.push({ role: "user", content: [toolResultBlock] });
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
}
|
|
44
|
+
function translateTools(tools) {
|
|
45
|
+
return tools.map((t) => ({
|
|
46
|
+
name: t.name,
|
|
47
|
+
description: t.description,
|
|
48
|
+
// as: harness Tool.inputSchema is Record<string,unknown>; SDK requires InputSchema with type:'object'
|
|
49
|
+
// which is always present at runtime — cannot be statically verified from the generic type
|
|
50
|
+
input_schema: t.inputSchema
|
|
51
|
+
}));
|
|
52
|
+
}
|
|
53
|
+
function mapStopReason(stopReason) {
|
|
54
|
+
if (stopReason === "end_turn") return "end";
|
|
55
|
+
if (stopReason === "tool_use") return "tool_use";
|
|
56
|
+
if (stopReason === "max_tokens") return "max_tokens";
|
|
57
|
+
return "end";
|
|
58
|
+
}
|
|
59
|
+
function normalizeResponse(response) {
|
|
60
|
+
let text = "";
|
|
61
|
+
const toolCalls = [];
|
|
62
|
+
for (const block of response.content) {
|
|
63
|
+
if (block.type === "text") {
|
|
64
|
+
text += block.text;
|
|
65
|
+
} else if (block.type === "tool_use") {
|
|
66
|
+
toolCalls.push({ id: block.id, name: block.name, input: block.input });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return {
|
|
70
|
+
text,
|
|
71
|
+
toolCalls,
|
|
72
|
+
stopReason: mapStopReason(response.stop_reason ?? "")
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
var ZEROED_STEP_CONTEXT = { agentId: "", sessionId: "", stepName: "" };
|
|
76
|
+
var Claude = class {
|
|
77
|
+
client;
|
|
78
|
+
model;
|
|
79
|
+
observer = {};
|
|
80
|
+
stepContext = ZEROED_STEP_CONTEXT;
|
|
81
|
+
/**
|
|
82
|
+
* @param model - Anthropic model ID, e.g. `'claude-3-5-haiku-20241022'`.
|
|
83
|
+
* @param options - Optional API key override.
|
|
84
|
+
*/
|
|
85
|
+
constructor(model, options) {
|
|
86
|
+
this.model = model;
|
|
87
|
+
this.client = new Anthropic({ apiKey: options?.apiKey });
|
|
88
|
+
}
|
|
89
|
+
bindObserver(observer) {
|
|
90
|
+
this.observer = observer;
|
|
91
|
+
}
|
|
92
|
+
setStepContext(ctx) {
|
|
93
|
+
this.stepContext = ctx;
|
|
94
|
+
}
|
|
95
|
+
async invoke(messages, options) {
|
|
96
|
+
const translatedMessages = translateMessages(messages);
|
|
97
|
+
const tools = options?.tools;
|
|
98
|
+
const response = await this.client.messages.create({
|
|
99
|
+
model: this.model,
|
|
100
|
+
messages: translatedMessages,
|
|
101
|
+
max_tokens: 4096,
|
|
102
|
+
...tools !== void 0 ? { tools: translateTools(tools) } : {}
|
|
103
|
+
});
|
|
104
|
+
const result = normalizeResponse(response);
|
|
105
|
+
const event = {
|
|
106
|
+
tokens: { input: response.usage.input_tokens, output: response.usage.output_tokens },
|
|
107
|
+
modelId: this.model,
|
|
108
|
+
stopReason: result.stopReason,
|
|
109
|
+
providerName: "anthropic"
|
|
110
|
+
};
|
|
111
|
+
this.observer.onEvent?.(this.stepContext, "llm.response", event);
|
|
112
|
+
return result;
|
|
113
|
+
}
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
// src/mock-claude.ts
|
|
117
|
+
var MockClaudeEmptyQueueError = class extends Error {
|
|
118
|
+
constructor() {
|
|
119
|
+
super("MockClaude has no responses configured \u2014 call new MockClaude(response) or enqueue(response) before invoke");
|
|
120
|
+
this.name = "MockClaudeEmptyQueueError";
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var ZEROED_STEP_CONTEXT2 = { agentId: "", sessionId: "", stepName: "" };
|
|
124
|
+
var MockClaude = class {
|
|
125
|
+
/** The message list from the most recent `invoke()` call. */
|
|
126
|
+
lastMessages = [];
|
|
127
|
+
queue = [];
|
|
128
|
+
observer = {};
|
|
129
|
+
stepContext = ZEROED_STEP_CONTEXT2;
|
|
130
|
+
/**
|
|
131
|
+
* @param responses - One or more responses to queue up front. May also be
|
|
132
|
+
* added later via {@link enqueue}.
|
|
133
|
+
*/
|
|
134
|
+
constructor(responses) {
|
|
135
|
+
if (responses !== void 0) {
|
|
136
|
+
this.enqueue(responses);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
/** Add one or more responses to the end of the queue. */
|
|
140
|
+
enqueue(response) {
|
|
141
|
+
const items = Array.isArray(response) ? response : [response];
|
|
142
|
+
this.queue.push(...items);
|
|
143
|
+
}
|
|
144
|
+
bindObserver(observer) {
|
|
145
|
+
this.observer = observer;
|
|
146
|
+
}
|
|
147
|
+
setStepContext(ctx) {
|
|
148
|
+
this.stepContext = ctx;
|
|
149
|
+
}
|
|
150
|
+
async invoke(messages, options) {
|
|
151
|
+
void options;
|
|
152
|
+
if (this.queue.length === 0) {
|
|
153
|
+
throw new MockClaudeEmptyQueueError();
|
|
154
|
+
}
|
|
155
|
+
const response = this.queue.length > 1 ? this.queue.shift() : this.queue[0];
|
|
156
|
+
this.lastMessages = messages;
|
|
157
|
+
const event = {
|
|
158
|
+
tokens: { input: 0, output: 0 },
|
|
159
|
+
modelId: "mock",
|
|
160
|
+
stopReason: response.stopReason,
|
|
161
|
+
providerName: "mock"
|
|
162
|
+
};
|
|
163
|
+
this.observer.onEvent?.(this.stepContext, "llm.response", event);
|
|
164
|
+
return response;
|
|
165
|
+
}
|
|
166
|
+
};
|
|
167
|
+
export {
|
|
168
|
+
Claude,
|
|
169
|
+
MockClaude,
|
|
170
|
+
MockClaudeEmptyQueueError
|
|
171
|
+
};
|
|
172
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/claude.ts","../src/mock-claude.ts"],"sourcesContent":["import type { LLM, Message, Tool, ToolCall, LLMResponse, LLMUsageEvent } from '@noetaris/harness-types'\nimport type { ObserverAware, Observer, StepContext } from '@noetaris/harness'\nimport Anthropic from '@anthropic-ai/sdk'\nimport type { Tool as AnthropicSDKTool } from '@anthropic-ai/sdk/resources/messages/messages.js'\n\n/** Options for {@link Claude}. */\nexport interface ClaudeOptions {\n /** Anthropic API key. Defaults to the `ANTHROPIC_API_KEY` environment variable. */\n apiKey?: string\n}\n\ntype AnthropicContentBlock =\n | { type: 'text'; text: string }\n | { type: 'tool_use'; id: string; name: string; input: unknown }\n | { type: 'tool_result'; tool_use_id: string; content: string }\n\ntype AnthropicMessage = {\n role: 'user' | 'assistant'\n content: string | AnthropicContentBlock[]\n}\n\n// Use SDK's Tool type for the translated tools array passed to client.messages.create\ntype AnthropicTool = AnthropicSDKTool\n\nfunction translateMessages(messages: Message[]): AnthropicMessage[] {\n const result: AnthropicMessage[] = []\n\n for (const msg of messages) {\n if (msg.role === 'user') {\n result.push({ role: 'user', content: msg.content })\n } else if (msg.role === 'assistant') {\n if (msg.toolCalls && msg.toolCalls.length > 0) {\n const blocks: AnthropicContentBlock[] = []\n if (msg.content) {\n blocks.push({ type: 'text', text: msg.content })\n }\n for (const tc of msg.toolCalls) {\n blocks.push({ type: 'tool_use', id: tc.id, name: tc.name, input: tc.input })\n }\n result.push({ role: 'assistant', content: blocks })\n } else {\n result.push({ role: 'assistant', content: msg.content ?? '' })\n }\n } else if (msg.role === 'tool') {\n const toolResultBlock: AnthropicContentBlock = {\n type: 'tool_result',\n tool_use_id: msg.toolCallId,\n content: msg.content,\n }\n\n const last = result[result.length - 1]\n if (last !== undefined && last.role === 'user') {\n // last message is a user message — merge into it\n if (typeof last.content === 'string') {\n // convert string content to array with text block + tool_result\n last.content = [\n { type: 'text', text: last.content },\n toolResultBlock,\n ]\n } else {\n last.content.push(toolResultBlock)\n }\n } else {\n // no preceding user message — wrap in a new user turn\n result.push({ role: 'user', content: [toolResultBlock] })\n }\n }\n }\n\n return result\n}\n\nfunction translateTools(tools: Tool[]): AnthropicTool[] {\n return tools.map((t) => ({\n name: t.name,\n description: t.description,\n // as: harness Tool.inputSchema is Record<string,unknown>; SDK requires InputSchema with type:'object'\n // which is always present at runtime — cannot be statically verified from the generic type\n input_schema: t.inputSchema as AnthropicSDKTool['input_schema'],\n }))\n}\n\nfunction mapStopReason(stopReason: string): LLMResponse['stopReason'] {\n if (stopReason === 'end_turn') return 'end'\n if (stopReason === 'tool_use') return 'tool_use'\n if (stopReason === 'max_tokens') return 'max_tokens'\n return 'end'\n}\n\nfunction normalizeResponse(response: Anthropic.Message): LLMResponse {\n let text = ''\n const toolCalls: ToolCall[] = []\n\n for (const block of response.content) {\n if (block.type === 'text') {\n text += block.text\n } else if (block.type === 'tool_use') {\n toolCalls.push({ id: block.id, name: block.name, input: block.input })\n }\n }\n\n return {\n text,\n toolCalls,\n stopReason: mapStopReason(response.stop_reason ?? ''),\n }\n}\n\nconst ZEROED_STEP_CONTEXT: StepContext = { agentId: '', sessionId: '', stepName: '' }\n\n/**\n * {@link LLM} adapter for the Anthropic Messages API.\n *\n * Implements {@link ObserverAware} — when an observer is bound the adapter\n * emits an `'llm.response'` event carrying an `LLMUsageEvent` payload after\n * each successful invocation.\n *\n * @example\n * ```ts\n * const llm = new Claude('claude-3-5-haiku-20241022')\n * const response = await llm.invoke(messages)\n * ```\n */\nexport class Claude implements LLM, ObserverAware {\n private readonly client: Anthropic\n private readonly model: string\n private observer: Observer = {}\n private stepContext: StepContext = ZEROED_STEP_CONTEXT\n\n /**\n * @param model - Anthropic model ID, e.g. `'claude-3-5-haiku-20241022'`.\n * @param options - Optional API key override.\n */\n constructor(model: string, options?: ClaudeOptions) {\n this.model = model\n this.client = new Anthropic({ apiKey: options?.apiKey })\n }\n\n bindObserver(observer: Observer): void {\n this.observer = observer\n }\n\n setStepContext(ctx: StepContext): void {\n this.stepContext = ctx\n }\n\n async invoke(messages: Message[], options?: { tools?: Tool[] }): Promise<LLMResponse> {\n const translatedMessages = translateMessages(messages)\n const tools = options?.tools\n\n const response = await this.client.messages.create({\n model: this.model,\n messages: translatedMessages,\n max_tokens: 4096,\n ...(tools !== undefined ? { tools: translateTools(tools) } : {}),\n })\n\n const result = normalizeResponse(response)\n\n const event: LLMUsageEvent = {\n tokens: { input: response.usage.input_tokens, output: response.usage.output_tokens },\n modelId: this.model,\n stopReason: result.stopReason,\n providerName: 'anthropic',\n }\n this.observer.onEvent?.(this.stepContext, 'llm.response', event)\n\n return result\n }\n}\n","import type { LLM, Message, Tool, LLMResponse, LLMUsageEvent } from '@noetaris/harness-types'\nimport type { ObserverAware, Observer, StepContext } from '@noetaris/harness'\n\n/** Thrown by {@link MockClaude} when `invoke` is called with no responses queued. */\nexport class MockClaudeEmptyQueueError extends Error {\n constructor() {\n super('MockClaude has no responses configured — call new MockClaude(response) or enqueue(response) before invoke')\n this.name = 'MockClaudeEmptyQueueError'\n }\n}\n\nconst ZEROED_STEP_CONTEXT: StepContext = { agentId: '', sessionId: '', stepName: '' }\n\n/**\n * In-memory test double for {@link Claude}.\n *\n * Pre-load one or more {@link LLMResponse} objects; each `invoke()` call\n * dequeues the next response. The last response is **sticky** — once only\n * one remains it repeats indefinitely rather than throwing.\n *\n * `lastMessages` is populated after each `invoke()` call, allowing assertions\n * on the conversation history passed to the adapter.\n *\n * @example\n * ```ts\n * const llm = new MockClaude({ text: 'hello', toolCalls: [], stopReason: 'end' })\n * ```\n */\nexport class MockClaude implements LLM, ObserverAware {\n /** The message list from the most recent `invoke()` call. */\n lastMessages: Message[] = []\n\n private queue: LLMResponse[] = []\n private observer: Observer = {}\n private stepContext: StepContext = ZEROED_STEP_CONTEXT\n\n /**\n * @param responses - One or more responses to queue up front. May also be\n * added later via {@link enqueue}.\n */\n constructor(responses?: LLMResponse | LLMResponse[]) {\n if (responses !== undefined) {\n this.enqueue(responses)\n }\n }\n\n /** Add one or more responses to the end of the queue. */\n enqueue(response: LLMResponse | LLMResponse[]): void {\n const items = Array.isArray(response) ? response : [response]\n this.queue.push(...items)\n }\n\n bindObserver(observer: Observer): void {\n this.observer = observer\n }\n\n setStepContext(ctx: StepContext): void {\n this.stepContext = ctx\n }\n\n async invoke(messages: Message[], options?: { tools?: Tool[] }): Promise<LLMResponse> {\n void options\n if (this.queue.length === 0) {\n throw new MockClaudeEmptyQueueError()\n }\n\n // sticky-last: dequeue only when more than one element remains\n const response: LLMResponse = this.queue.length > 1\n ? (this.queue.shift() as LLMResponse) // as: shift() on non-empty array is always defined; length > 1 is checked above\n : (this.queue[0] as LLMResponse) // as: queue.length === 1 guaranteed by the empty check; index 0 is always defined\n\n this.lastMessages = messages\n\n const event: LLMUsageEvent = {\n tokens: { input: 0, output: 0 },\n modelId: 'mock',\n stopReason: response.stopReason,\n providerName: 'mock',\n }\n this.observer.onEvent?.(this.stepContext, 'llm.response', event)\n\n return response\n }\n}\n"],"mappings":"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","names":["ZEROED_STEP_CONTEXT"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@noetaris/harness-anthropic",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Anthropic Claude adapter for @noetaris/harness",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/noetaris-lab/harness-anthropic.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/noetaris-lab/harness-anthropic#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/noetaris-lab/harness-anthropic/issues"
|
|
13
|
+
},
|
|
14
|
+
"main": "./dist/index.js",
|
|
15
|
+
"types": "./dist/index.d.ts",
|
|
16
|
+
"exports": {
|
|
17
|
+
".": {
|
|
18
|
+
"import": "./dist/index.js",
|
|
19
|
+
"types": "./dist/index.d.ts"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"engines": {
|
|
23
|
+
"node": ">=22"
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsup",
|
|
27
|
+
"typecheck": "tsc --noEmit",
|
|
28
|
+
"test": "vitest run",
|
|
29
|
+
"test:watch": "vitest",
|
|
30
|
+
"prepublishOnly": "pnpm build && pnpm test",
|
|
31
|
+
"publish:npm": "pnpm publish --access public --no-git-checks"
|
|
32
|
+
},
|
|
33
|
+
"files": [
|
|
34
|
+
"dist"
|
|
35
|
+
],
|
|
36
|
+
"pnpm": {
|
|
37
|
+
"onlyBuiltDependencies": [
|
|
38
|
+
"esbuild"
|
|
39
|
+
]
|
|
40
|
+
},
|
|
41
|
+
"peerDependencies": {
|
|
42
|
+
"@noetaris/harness": ">=0.1.0",
|
|
43
|
+
"@noetaris/harness-types": ">=0.1.0"
|
|
44
|
+
},
|
|
45
|
+
"devDependencies": {
|
|
46
|
+
"@noetaris/harness": "^0.3.0",
|
|
47
|
+
"@noetaris/harness-types": "^0.2.0",
|
|
48
|
+
"@types/node": "^25.8.0",
|
|
49
|
+
"tsup": "^8.5.1",
|
|
50
|
+
"typescript": "^6.0.3",
|
|
51
|
+
"vitest": "^4.1.6"
|
|
52
|
+
},
|
|
53
|
+
"dependencies": {
|
|
54
|
+
"@anthropic-ai/sdk": "^0.96.0"
|
|
55
|
+
}
|
|
56
|
+
}
|