@cognipeer/agent-sdk 0.8.8 → 0.8.10
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/index.cjs +38 -10
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -5
- package/dist/index.d.ts +48 -5
- package/dist/index.mjs +38 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -719,7 +719,8 @@ type ReflectionConfig = {
|
|
|
719
719
|
onReflection?: (record: ReflectionRecord, ctx: ReflectionHookContext) => void;
|
|
720
720
|
};
|
|
721
721
|
type NativeReasoningConfig = {
|
|
722
|
-
|
|
722
|
+
/** `none` is OFF — a value that gets SENT. See ReasoningEffort in providers/types.ts. */
|
|
723
|
+
effort?: "none" | "minimal" | "low" | "medium" | "high";
|
|
723
724
|
budgetTokens?: number;
|
|
724
725
|
includeThoughts?: boolean;
|
|
725
726
|
providerExtras?: Record<string, any>;
|
|
@@ -2481,6 +2482,8 @@ declare class StructuredOutputManager<T> {
|
|
|
2481
2482
|
/**
|
|
2482
2483
|
* Build a nudge message for when the model didn't produce structured output.
|
|
2483
2484
|
* Used by the agent loop when it needs to retry.
|
|
2485
|
+
* User role is intentional: OpenAI-compatible chat templates such as Qwen
|
|
2486
|
+
* reject system messages anywhere except the beginning of the conversation.
|
|
2484
2487
|
*/
|
|
2485
2488
|
buildNudgeMessage(isLastAttempt: boolean): {
|
|
2486
2489
|
role: string;
|
|
@@ -2916,7 +2919,18 @@ type ChatCompletionRequest = {
|
|
|
2916
2919
|
*/
|
|
2917
2920
|
reasoning?: ReasoningRequestConfig;
|
|
2918
2921
|
};
|
|
2919
|
-
|
|
2922
|
+
/**
|
|
2923
|
+
* `none` is OFF, and it is a value rather than the absence of one.
|
|
2924
|
+
*
|
|
2925
|
+
* On a thinking-by-default model — every Qwen3 under vLLM, gpt-5.1 and newer on
|
|
2926
|
+
* OpenAI — sending no reasoning field leaves the model thinking. So "do not
|
|
2927
|
+
* think" is an instruction that has to travel: `reasoning_effort:"none"` on
|
|
2928
|
+
* OpenAI, `thinkingBudget:0` on Gemini, and no thinking block at all on
|
|
2929
|
+
* Anthropic (where thinking is opt-in and the absence IS off). Omitting
|
|
2930
|
+
* `effort` still means "say nothing about reasoning", which is the different
|
|
2931
|
+
* and equally necessary state.
|
|
2932
|
+
*/
|
|
2933
|
+
type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high";
|
|
2920
2934
|
type ReasoningRequestConfig = {
|
|
2921
2935
|
/** Qualitative effort hint (mapped per-provider). */
|
|
2922
2936
|
effort?: ReasoningEffort;
|
|
@@ -2979,6 +2993,28 @@ type OpenAIProviderConfig = {
|
|
|
2979
2993
|
defaultModel?: string;
|
|
2980
2994
|
defaultHeaders?: Record<string, string>;
|
|
2981
2995
|
retry?: ProviderRetryConfig;
|
|
2996
|
+
/**
|
|
2997
|
+
* WHICH API a reasoning-carrying request is addressed to.
|
|
2998
|
+
*
|
|
2999
|
+
* auto (default, and today's behaviour) route to the Responses API when
|
|
3000
|
+
* the request carries `reasoning` AND the model name looks like an
|
|
3001
|
+
* o-series/gpt-5 reasoning model.
|
|
3002
|
+
* never always Chat Completions. `reasoning` is still mapped, as
|
|
3003
|
+
* `reasoning_effort` plus any `providerExtras`.
|
|
3004
|
+
* always always Responses, whatever the model is called.
|
|
3005
|
+
*
|
|
3006
|
+
* `auto` is a name-based guess, and it decides far more than where reasoning
|
|
3007
|
+
* goes: Responses is a different request shape (`input` not `messages`,
|
|
3008
|
+
* `text.format` not `response_format`, flat tool objects) and a different
|
|
3009
|
+
* response parse. A caller with its own measurements of the Chat Completions
|
|
3010
|
+
* body — a gateway, a proxy, an on-prem server that serves one and not the
|
|
3011
|
+
* other, or anything that has tuned `extra` — must be able to say so, rather
|
|
3012
|
+
* than discover that asking for less thinking silently changed the transport.
|
|
3013
|
+
*
|
|
3014
|
+
* Applies to any OpenAI-shaped endpoint, which is also why the guess is weak
|
|
3015
|
+
* where it matters most: a self-hosted model is named by whoever served it.
|
|
3016
|
+
*/
|
|
3017
|
+
responsesApi?: "auto" | "never" | "always";
|
|
2982
3018
|
};
|
|
2983
3019
|
type AnthropicProviderConfig = {
|
|
2984
3020
|
provider: "anthropic";
|
|
@@ -3083,10 +3119,17 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
3083
3119
|
protected readonly baseURL: string;
|
|
3084
3120
|
protected readonly defaultModel: string;
|
|
3085
3121
|
protected readonly defaultHeaders: Record<string, string>;
|
|
3122
|
+
protected readonly responsesApi: "auto" | "never" | "always";
|
|
3086
3123
|
constructor(config: OpenAIProviderConfig);
|
|
3087
|
-
/** True when
|
|
3088
|
-
*
|
|
3089
|
-
*
|
|
3124
|
+
/** True when this request should go to the Responses API rather than Chat
|
|
3125
|
+
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
3126
|
+
* Chat Completions silently drops.
|
|
3127
|
+
*
|
|
3128
|
+
* `auto` (the default) keeps the historical rule: only when the request
|
|
3129
|
+
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
3130
|
+
* model. `never`/`always` let a caller state the answer instead, because the
|
|
3131
|
+
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
3132
|
+
* see `responsesApi` in OpenAIProviderConfig. */
|
|
3090
3133
|
protected useResponsesApi(request: ChatCompletionRequest): boolean;
|
|
3091
3134
|
complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
3092
3135
|
completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
|
package/dist/index.d.ts
CHANGED
|
@@ -719,7 +719,8 @@ type ReflectionConfig = {
|
|
|
719
719
|
onReflection?: (record: ReflectionRecord, ctx: ReflectionHookContext) => void;
|
|
720
720
|
};
|
|
721
721
|
type NativeReasoningConfig = {
|
|
722
|
-
|
|
722
|
+
/** `none` is OFF — a value that gets SENT. See ReasoningEffort in providers/types.ts. */
|
|
723
|
+
effort?: "none" | "minimal" | "low" | "medium" | "high";
|
|
723
724
|
budgetTokens?: number;
|
|
724
725
|
includeThoughts?: boolean;
|
|
725
726
|
providerExtras?: Record<string, any>;
|
|
@@ -2481,6 +2482,8 @@ declare class StructuredOutputManager<T> {
|
|
|
2481
2482
|
/**
|
|
2482
2483
|
* Build a nudge message for when the model didn't produce structured output.
|
|
2483
2484
|
* Used by the agent loop when it needs to retry.
|
|
2485
|
+
* User role is intentional: OpenAI-compatible chat templates such as Qwen
|
|
2486
|
+
* reject system messages anywhere except the beginning of the conversation.
|
|
2484
2487
|
*/
|
|
2485
2488
|
buildNudgeMessage(isLastAttempt: boolean): {
|
|
2486
2489
|
role: string;
|
|
@@ -2916,7 +2919,18 @@ type ChatCompletionRequest = {
|
|
|
2916
2919
|
*/
|
|
2917
2920
|
reasoning?: ReasoningRequestConfig;
|
|
2918
2921
|
};
|
|
2919
|
-
|
|
2922
|
+
/**
|
|
2923
|
+
* `none` is OFF, and it is a value rather than the absence of one.
|
|
2924
|
+
*
|
|
2925
|
+
* On a thinking-by-default model — every Qwen3 under vLLM, gpt-5.1 and newer on
|
|
2926
|
+
* OpenAI — sending no reasoning field leaves the model thinking. So "do not
|
|
2927
|
+
* think" is an instruction that has to travel: `reasoning_effort:"none"` on
|
|
2928
|
+
* OpenAI, `thinkingBudget:0` on Gemini, and no thinking block at all on
|
|
2929
|
+
* Anthropic (where thinking is opt-in and the absence IS off). Omitting
|
|
2930
|
+
* `effort` still means "say nothing about reasoning", which is the different
|
|
2931
|
+
* and equally necessary state.
|
|
2932
|
+
*/
|
|
2933
|
+
type ReasoningEffort = "none" | "minimal" | "low" | "medium" | "high";
|
|
2920
2934
|
type ReasoningRequestConfig = {
|
|
2921
2935
|
/** Qualitative effort hint (mapped per-provider). */
|
|
2922
2936
|
effort?: ReasoningEffort;
|
|
@@ -2979,6 +2993,28 @@ type OpenAIProviderConfig = {
|
|
|
2979
2993
|
defaultModel?: string;
|
|
2980
2994
|
defaultHeaders?: Record<string, string>;
|
|
2981
2995
|
retry?: ProviderRetryConfig;
|
|
2996
|
+
/**
|
|
2997
|
+
* WHICH API a reasoning-carrying request is addressed to.
|
|
2998
|
+
*
|
|
2999
|
+
* auto (default, and today's behaviour) route to the Responses API when
|
|
3000
|
+
* the request carries `reasoning` AND the model name looks like an
|
|
3001
|
+
* o-series/gpt-5 reasoning model.
|
|
3002
|
+
* never always Chat Completions. `reasoning` is still mapped, as
|
|
3003
|
+
* `reasoning_effort` plus any `providerExtras`.
|
|
3004
|
+
* always always Responses, whatever the model is called.
|
|
3005
|
+
*
|
|
3006
|
+
* `auto` is a name-based guess, and it decides far more than where reasoning
|
|
3007
|
+
* goes: Responses is a different request shape (`input` not `messages`,
|
|
3008
|
+
* `text.format` not `response_format`, flat tool objects) and a different
|
|
3009
|
+
* response parse. A caller with its own measurements of the Chat Completions
|
|
3010
|
+
* body — a gateway, a proxy, an on-prem server that serves one and not the
|
|
3011
|
+
* other, or anything that has tuned `extra` — must be able to say so, rather
|
|
3012
|
+
* than discover that asking for less thinking silently changed the transport.
|
|
3013
|
+
*
|
|
3014
|
+
* Applies to any OpenAI-shaped endpoint, which is also why the guess is weak
|
|
3015
|
+
* where it matters most: a self-hosted model is named by whoever served it.
|
|
3016
|
+
*/
|
|
3017
|
+
responsesApi?: "auto" | "never" | "always";
|
|
2982
3018
|
};
|
|
2983
3019
|
type AnthropicProviderConfig = {
|
|
2984
3020
|
provider: "anthropic";
|
|
@@ -3083,10 +3119,17 @@ declare class OpenAIProvider extends BaseProvider {
|
|
|
3083
3119
|
protected readonly baseURL: string;
|
|
3084
3120
|
protected readonly defaultModel: string;
|
|
3085
3121
|
protected readonly defaultHeaders: Record<string, string>;
|
|
3122
|
+
protected readonly responsesApi: "auto" | "never" | "always";
|
|
3086
3123
|
constructor(config: OpenAIProviderConfig);
|
|
3087
|
-
/** True when
|
|
3088
|
-
*
|
|
3089
|
-
*
|
|
3124
|
+
/** True when this request should go to the Responses API rather than Chat
|
|
3125
|
+
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
3126
|
+
* Chat Completions silently drops.
|
|
3127
|
+
*
|
|
3128
|
+
* `auto` (the default) keeps the historical rule: only when the request
|
|
3129
|
+
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
3130
|
+
* model. `never`/`always` let a caller state the answer instead, because the
|
|
3131
|
+
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
3132
|
+
* see `responsesApi` in OpenAIProviderConfig. */
|
|
3090
3133
|
protected useResponsesApi(request: ChatCompletionRequest): boolean;
|
|
3091
3134
|
complete(request: ChatCompletionRequest): Promise<ChatCompletionResponse>;
|
|
3092
3135
|
completeStream(request: ChatCompletionRequest): AsyncGenerator<ChatCompletionChunk, void, unknown>;
|
package/dist/index.mjs
CHANGED
|
@@ -4289,7 +4289,7 @@ var LEVEL_DEFAULTS = {
|
|
|
4289
4289
|
};
|
|
4290
4290
|
var VALID_LEVELS = ["minimal", "low", "medium", "high"];
|
|
4291
4291
|
var VALID_CADENCES = ["off", "every_turn", "after_tool", "on_branch", "initial_then_after_tool"];
|
|
4292
|
-
var VALID_EFFORTS = ["minimal", "low", "medium", "high"];
|
|
4292
|
+
var VALID_EFFORTS = ["none", "minimal", "low", "medium", "high"];
|
|
4293
4293
|
function validateReasoningConfig(input) {
|
|
4294
4294
|
if (!input) return;
|
|
4295
4295
|
if (input.level !== void 0 && !VALID_LEVELS.includes(input.level)) {
|
|
@@ -4875,11 +4875,13 @@ var StructuredOutputManager = class {
|
|
|
4875
4875
|
/**
|
|
4876
4876
|
* Build a nudge message for when the model didn't produce structured output.
|
|
4877
4877
|
* Used by the agent loop when it needs to retry.
|
|
4878
|
+
* User role is intentional: OpenAI-compatible chat templates such as Qwen
|
|
4879
|
+
* reject system messages anywhere except the beginning of the conversation.
|
|
4878
4880
|
*/
|
|
4879
4881
|
buildNudgeMessage(isLastAttempt) {
|
|
4880
4882
|
if (this.strategy.kind === "native") {
|
|
4881
4883
|
return {
|
|
4882
|
-
role: "
|
|
4884
|
+
role: "user",
|
|
4883
4885
|
content: [
|
|
4884
4886
|
"A structured output schema is active.",
|
|
4885
4887
|
"You MUST respond with valid JSON matching the schema.",
|
|
@@ -4889,7 +4891,7 @@ var StructuredOutputManager = class {
|
|
|
4889
4891
|
};
|
|
4890
4892
|
}
|
|
4891
4893
|
return {
|
|
4892
|
-
role: "
|
|
4894
|
+
role: "user",
|
|
4893
4895
|
content: [
|
|
4894
4896
|
"A structured output schema is active.",
|
|
4895
4897
|
"You MUST now call tool `response` with the final JSON object that matches the schema.",
|
|
@@ -4907,7 +4909,7 @@ var StructuredOutputManager = class {
|
|
|
4907
4909
|
const fieldIssues = error.fieldErrors.map((f) => `- "${f.path}": ${f.message} (received: ${JSON.stringify(f.received)})`).join("\n");
|
|
4908
4910
|
const action = this.strategy.kind === "native" ? "Please output corrected JSON matching the schema." : "Please call `response` again with corrected values matching the schema.";
|
|
4909
4911
|
return {
|
|
4910
|
-
role: "
|
|
4912
|
+
role: "user",
|
|
4911
4913
|
content: [
|
|
4912
4914
|
"Your previous structured output had validation errors:",
|
|
4913
4915
|
fieldIssues,
|
|
@@ -8781,12 +8783,19 @@ function parseSSEBlock(block) {
|
|
|
8781
8783
|
|
|
8782
8784
|
// src/providers/utils/reasoning.ts
|
|
8783
8785
|
var ANTHROPIC_EFFORT_BUDGET = {
|
|
8786
|
+
// 0 = off. `applyAnthropicReasoning` bails on a falsy budget and emits no
|
|
8787
|
+
// thinking block, which on Anthropic IS off — thinking there is opt-in.
|
|
8788
|
+
none: 0,
|
|
8784
8789
|
minimal: 1024,
|
|
8785
8790
|
low: 2048,
|
|
8786
8791
|
medium: 8192,
|
|
8787
8792
|
high: 16384
|
|
8788
8793
|
};
|
|
8789
8794
|
var GEMINI_EFFORT_BUDGET = {
|
|
8795
|
+
// 0 is Gemini's own documented spelling for "do not think", and unlike
|
|
8796
|
+
// Anthropic it has to be SENT — hence `applyGeminiReasoning` testing for null
|
|
8797
|
+
// rather than falsiness.
|
|
8798
|
+
none: 0,
|
|
8790
8799
|
minimal: 512,
|
|
8791
8800
|
low: 1024,
|
|
8792
8801
|
medium: 4096,
|
|
@@ -8862,14 +8871,15 @@ function clampBudget(budget, maxTokens) {
|
|
|
8862
8871
|
}
|
|
8863
8872
|
function normalizeEffort(effort) {
|
|
8864
8873
|
if (!effort) return void 0;
|
|
8865
|
-
const allowed = ["minimal", "low", "medium", "high"];
|
|
8874
|
+
const allowed = ["none", "minimal", "low", "medium", "high"];
|
|
8866
8875
|
return allowed.includes(effort) ? effort : void 0;
|
|
8867
8876
|
}
|
|
8868
8877
|
function resolveBudget(reasoning, effortBudget) {
|
|
8878
|
+
const effort = normalizeEffort(reasoning.effort);
|
|
8879
|
+
if (effort === "none") return 0;
|
|
8869
8880
|
if (typeof reasoning.budgetTokens === "number" && reasoning.budgetTokens > 0) {
|
|
8870
8881
|
return Math.floor(reasoning.budgetTokens);
|
|
8871
8882
|
}
|
|
8872
|
-
const effort = normalizeEffort(reasoning.effort);
|
|
8873
8883
|
if (effort && effortBudget[effort]) return effortBudget[effort];
|
|
8874
8884
|
return void 0;
|
|
8875
8885
|
}
|
|
@@ -8958,12 +8968,21 @@ var OpenAIProvider = class extends BaseProvider {
|
|
|
8958
8968
|
...config.defaultHeaders,
|
|
8959
8969
|
...config.organization ? { "OpenAI-Organization": config.organization } : {}
|
|
8960
8970
|
};
|
|
8971
|
+
this.responsesApi = config.responsesApi ?? "auto";
|
|
8961
8972
|
if (config.retry) this.retryPolicy = { ...this.retryPolicy, ...config.retry };
|
|
8962
8973
|
}
|
|
8963
|
-
/** True when
|
|
8964
|
-
*
|
|
8965
|
-
*
|
|
8974
|
+
/** True when this request should go to the Responses API rather than Chat
|
|
8975
|
+
* Completions — which exposes `reasoning.effort` and reasoning summaries that
|
|
8976
|
+
* Chat Completions silently drops.
|
|
8977
|
+
*
|
|
8978
|
+
* `auto` (the default) keeps the historical rule: only when the request
|
|
8979
|
+
* carries a reasoning config AND the model name looks like an o-series/gpt-5
|
|
8980
|
+
* model. `never`/`always` let a caller state the answer instead, because the
|
|
8981
|
+
* name check is a guess and the two APIs differ in far more than reasoning —
|
|
8982
|
+
* see `responsesApi` in OpenAIProviderConfig. */
|
|
8966
8983
|
useResponsesApi(request) {
|
|
8984
|
+
if (this.responsesApi === "never") return false;
|
|
8985
|
+
if (this.responsesApi === "always") return true;
|
|
8967
8986
|
if (!request.reasoning) return false;
|
|
8968
8987
|
return isReasoningModel(request.model || this.defaultModel);
|
|
8969
8988
|
}
|
|
@@ -10826,7 +10845,7 @@ function fromNativeProvider(provider, options) {
|
|
|
10826
10845
|
if (tools?.length) req.tools = tools;
|
|
10827
10846
|
if (opts?.extra) req.extra = opts.extra;
|
|
10828
10847
|
const reasoningOverride = invokeOptions?.reasoning;
|
|
10829
|
-
const reasoningCfg =
|
|
10848
|
+
const reasoningCfg = mergeReasoning(opts?.reasoning, reasoningOverride);
|
|
10830
10849
|
if (reasoningCfg) req.reasoning = reasoningCfg;
|
|
10831
10850
|
const tc = invokeOptions?.tool_choice ?? invokeOptions?.toolChoice;
|
|
10832
10851
|
if (tc && tools?.length) {
|
|
@@ -10855,6 +10874,15 @@ function fromNativeProvider(provider, options) {
|
|
|
10855
10874
|
return req;
|
|
10856
10875
|
}
|
|
10857
10876
|
}
|
|
10877
|
+
function mergeReasoning(base, override) {
|
|
10878
|
+
if (!base) return override;
|
|
10879
|
+
if (!override) return base;
|
|
10880
|
+
const merged = { ...base, ...override };
|
|
10881
|
+
if (base.providerExtras || override.providerExtras) {
|
|
10882
|
+
merged.providerExtras = { ...base.providerExtras, ...override.providerExtras };
|
|
10883
|
+
}
|
|
10884
|
+
return merged;
|
|
10885
|
+
}
|
|
10858
10886
|
function toUnifiedMessage(msg) {
|
|
10859
10887
|
const unified = {
|
|
10860
10888
|
role: msg.role,
|