@hunterzhu/pulse-adapters 0.1.4 → 0.1.6

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.
@@ -1,4 +1,13 @@
1
1
  import { consumeProviderSse, normalizeAnthropicResponse, parseProviderJson, providerHttpErrorFromResponse, providerNetworkError, providerResponseError } from './normalize.js';
2
+ function anthropicThinking(effort, maxTokens) {
3
+ if (!effort)
4
+ return {};
5
+ const requested = effort === 'high' ? 8_000 : effort === 'medium' ? 2_048 : 1_024;
6
+ const budget = Math.min(requested, maxTokens - 1_024);
7
+ if (budget < 1_024 || budget >= maxTokens)
8
+ return {};
9
+ return { thinking: { type: 'enabled', budget_tokens: budget } };
10
+ }
2
11
  export class AnthropicAdapter {
3
12
  id;
4
13
  config;
@@ -12,7 +21,8 @@ export class AnthropicAdapter {
12
21
  const messages = [{ role: 'user', content: params.request.blocks.filter((block) => !['system', 'policy', 'tools'].includes(block.kind)).map((block) => ({ type: 'text', text: typeof block.content === 'string' ? block.content : JSON.stringify(block.content) })) }];
13
22
  const streaming = params.onObservation !== undefined;
14
23
  const tools = toolDefinitions(params.request);
15
- const body = { ...(params.model ?? this.config.defaultModel ? { model: params.model ?? this.config.defaultModel } : {}), max_tokens: params.maxOutputTokens ?? this.config.maxOutputTokens ?? 4096, ...(system ? { system } : {}), messages, ...(tools.length ? { tools, ...(this.config.toolChoice === undefined ? {} : { tool_choice: anthropicToolChoice(this.config.toolChoice) }) } : {}), ...(params.outputSchema === undefined ? {} : { output_format: { type: 'json_schema', schema: params.outputSchema } }), ...(streaming ? { stream: true } : {}) };
24
+ const maxTokens = params.maxOutputTokens ?? this.config.maxOutputTokens ?? 4096;
25
+ const body = { ...(params.model ?? this.config.defaultModel ? { model: params.model ?? this.config.defaultModel } : {}), max_tokens: maxTokens, ...anthropicThinking(this.config.reasoningEffort, maxTokens), ...(system ? { system } : {}), messages, ...(tools.length ? { tools, ...(this.config.toolChoice === undefined ? {} : { tool_choice: anthropicToolChoice(this.config.toolChoice) }) } : {}), ...(params.outputSchema === undefined ? {} : { output_format: { type: 'json_schema', schema: params.outputSchema } }), ...(streaming ? { stream: true } : {}) };
16
26
  try {
17
27
  const response = await fetch(`${(this.config.baseURL ?? 'https://api.anthropic.com').replace(/\/$/, '')}/v1/messages`, { method: 'POST', signal: params.signal, headers: { 'content-type': 'application/json', ...(this.config.apiKey ? { 'x-api-key': this.config.apiKey } : {}), 'anthropic-version': '2023-06-01' }, body: JSON.stringify(body) });
18
28
  if (!response.ok)
@@ -1,10 +1,11 @@
1
- import type { JsonValue, LLMRequestProjection } from '@hunterzhu/pulse-runtime';
1
+ import type { JsonValue, LLMRequestProjection, LLMResult } from '@hunterzhu/pulse-runtime';
2
2
  import type { ProviderAdapter, ProviderPresetConfig } from './types.js';
3
3
  export declare class OpenAICompatibleAdapter implements ProviderAdapter {
4
4
  readonly id: string;
5
5
  private readonly config;
6
6
  readonly name = "OpenAI Compatible";
7
7
  private readonly baseURL;
8
+ private reasoningUnsupported;
8
9
  constructor(id: string, config: ProviderPresetConfig);
9
10
  executeAttempt(params: {
10
11
  request: LLMRequestProjection;
@@ -13,7 +14,7 @@ export declare class OpenAICompatibleAdapter implements ProviderAdapter {
13
14
  model?: string;
14
15
  outputSchema?: JsonValue;
15
16
  maxOutputTokens?: number;
16
- }): Promise<import("@hunterzhu/pulse-runtime").LLMResult>;
17
+ }): Promise<LLMResult>;
17
18
  }
18
19
  export declare function toOpenAIMessages(request: LLMRequestProjection): Array<{
19
20
  role: 'system' | 'user' | 'assistant';
@@ -1,9 +1,18 @@
1
1
  import { consumeProviderSse, normalizeOpenAIResponse, parseProviderJson, providerHttpErrorFromResponse, providerNetworkError } from './normalize.js';
2
+ function reasoningParameterRejected(cause) {
3
+ if (!(cause instanceof Error))
4
+ return false;
5
+ const code = cause.code;
6
+ if (code !== 'PROVIDER_HTTP_400')
7
+ return false;
8
+ return /reasoning_effort|unknown parameter|unrecognized (?:request )?argument|extra fields? not permitted/i.test(cause.message);
9
+ }
2
10
  export class OpenAICompatibleAdapter {
3
11
  id;
4
12
  config;
5
13
  name = 'OpenAI Compatible';
6
14
  baseURL;
15
+ reasoningUnsupported = false;
7
16
  constructor(id, config) {
8
17
  this.id = id;
9
18
  this.config = config;
@@ -12,7 +21,8 @@ export class OpenAICompatibleAdapter {
12
21
  async executeAttempt(params) {
13
22
  const streaming = params.onObservation !== undefined;
14
23
  const { definitions: tools, aliases: toolNameAliases } = toolDefinitions(params.request);
15
- const body = { ...(params.model ?? this.config.defaultModel ? { model: params.model ?? this.config.defaultModel } : {}), ...((params.maxOutputTokens ?? this.config.maxOutputTokens) === undefined ? {} : { max_tokens: params.maxOutputTokens ?? this.config.maxOutputTokens }), messages: toMessages(params.request), ...(tools.length ? { tools, ...(this.config.toolChoice === undefined ? {} : { tool_choice: this.config.toolChoice }) } : {}), ...(params.outputSchema === undefined ? {} : { response_format: { type: 'json_schema', json_schema: { name: 'pulse_output', strict: true, schema: params.outputSchema } } }), ...(streaming ? { stream: true, stream_options: { include_usage: true } } : {}) };
24
+ const includeReasoning = Boolean(this.config.reasoningEffort) && !this.reasoningUnsupported;
25
+ const body = { ...(params.model ?? this.config.defaultModel ? { model: params.model ?? this.config.defaultModel } : {}), ...((params.maxOutputTokens ?? this.config.maxOutputTokens) === undefined ? {} : { max_tokens: params.maxOutputTokens ?? this.config.maxOutputTokens }), ...(includeReasoning ? { reasoning_effort: this.config.reasoningEffort } : {}), messages: toMessages(params.request), ...(tools.length ? { tools, ...(this.config.toolChoice === undefined ? {} : { tool_choice: this.config.toolChoice }) } : {}), ...(params.outputSchema === undefined ? {} : { response_format: { type: 'json_schema', json_schema: { name: 'pulse_output', strict: true, schema: params.outputSchema } } }), ...(streaming ? { stream: true, stream_options: { include_usage: true } } : {}) };
16
26
  try {
17
27
  const response = await fetch(`${this.baseURL.replace(/\/$/, '')}/chat/completions`, { method: 'POST', signal: params.signal, headers: { 'content-type': 'application/json', ...(this.config.apiKey ? { authorization: `Bearer ${this.config.apiKey}` } : {}), ...(this.config.extraHeaders ?? {}) }, body: JSON.stringify(body) });
18
28
  if (!response.ok)
@@ -60,6 +70,10 @@ export class OpenAICompatibleAdapter {
60
70
  catch (cause) {
61
71
  if (params.signal.aborted)
62
72
  throw Object.assign(new Error('Provider request was cancelled.'), { code: 'PROVIDER_REQUEST_CANCELLED', retryable: false, cause });
73
+ if (includeReasoning && reasoningParameterRejected(cause)) {
74
+ this.reasoningUnsupported = true;
75
+ return this.executeAttempt(params);
76
+ }
63
77
  if (cause instanceof Error && 'code' in cause && typeof cause.code === 'string' && 'retryable' in cause && typeof cause.retryable === 'boolean')
64
78
  throw cause;
65
79
  throw providerNetworkError(cause);
@@ -25,4 +25,5 @@ export interface ProviderPresetConfig {
25
25
  maxOutputTokens?: number;
26
26
  toolChoice?: ProviderToolChoice;
27
27
  extraHeaders?: Record<string, string>;
28
+ reasoningEffort?: 'low' | 'medium' | 'high' | undefined;
28
29
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@hunterzhu/pulse-adapters",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/zhuhengtan/Pulse"
@@ -16,7 +16,7 @@
16
16
  "registry": "https://registry.npmjs.org"
17
17
  },
18
18
  "dependencies": {
19
- "@hunterzhu/pulse-runtime": "0.1.4",
20
- "@hunterzhu/pulse-tool-sdk": "0.1.4"
19
+ "@hunterzhu/pulse-runtime": "0.1.6",
20
+ "@hunterzhu/pulse-tool-sdk": "0.1.6"
21
21
  }
22
22
  }