@oh-my-pi/pi-catalog 16.1.22 → 16.2.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/CHANGELOG.md CHANGED
@@ -2,6 +2,31 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [16.2.0] - 2026-06-27
6
+
7
+ ### Added
8
+
9
+ - Added GitLab Duo Agent catalog discovery, including namespace selection, live model mapping, and a bundled fallback model for fresh installs.
10
+ - Added OpenAICompat.supportsNamedToolChoice to support forced tool use on string-only OpenAI-compatible chat servers without emitting the named function-object tool_choice shape.
11
+ - Added model metadata support for provider-native remote compaction and compaction-only model selection.
12
+
13
+ ### Changed
14
+
15
+ - Disabled the thinking-effort selector for GitLab Duo Agent models since the underlying platform parameters are server-fixed.
16
+
17
+ ### Fixed
18
+
19
+ - Improved GitLab Duo Agent and Duo Workflow namespace and project discovery to robustly handle paginated groups, SSH remotes with custom ports, Git worktrees, self-managed GitLab instances with relative paths, and configuration via GITLAB_DUO_PROJECT_PATH or GITLAB_DUO_PROJECT_ID.
20
+ - Fixed built-in LiteLLM discovery to prefer rich proxy metadata from management endpoints and avoid caching stale capability data.
21
+ - Fixed GitLab Duo Workflow model specifications to resolve correct static context windows, enabling accurate context usage tracking and auto-compaction.
22
+
23
+ ## [16.1.23] - 2026-06-26
24
+
25
+ ### Added
26
+
27
+ - Added `OpenAICompat.qwenPreserveThinking` — auto-enabled when the resolved `thinkingFormat` is `"qwen"` or `"qwen-chat-template"` AND `replayReasoningContent` is on (i.e. the four built-in local OpenAI-compatible providers, or a custom provider pointed at a loopback / RFC1918 / `*.local` baseUrl). Pairs with the chat-completions encoder change so the request body carries `preserve_thinking: true` (twin top-level + `chat_template_kwargs` emission), keeping Qwen3.6+ from stripping `<think>...</think>` off older assistant turns and breaking the local slot's KV cache between user messages. Non-Qwen chat templates ignore the parameter, so the flag stays a no-op outside the Qwen path; users on a cloud Qwen host (Alibaba Dashscope / Qwen Portal) can opt in with `compat.qwenPreserveThinking: true`. ([#3541](https://github.com/can1357/oh-my-pi/issues/3541))
28
+ - Added CoreWeave Serverless Inference as an OpenAI-compatible provider with models.dev-backed bundled catalog metadata.
29
+
5
30
  ## [16.1.22] - 2026-06-26
6
31
 
7
32
  ### Added
package/README.md CHANGED
@@ -24,7 +24,7 @@ Import from subpaths (`@oh-my-pi/pi-catalog/<module>`) or the root barrel.
24
24
  Never edit `src/models.json` by hand — it is produced from upstream sources (models.dev, provider catalog discovery, OpenCode docs) by `scripts/generate-models.ts` and the resolvers in `src/provider-models/`. Regenerate with:
25
25
 
26
26
  ```sh
27
- bun --cwd=packages/catalog run generate-models
27
+ bun run gen:models
28
28
  ```
29
29
 
30
30
  To change an entry, fix the source: resolver overrides in `provider-models/openai-compat.ts`, provider entries in `provider-models/descriptors.ts`, generator fixups in `scripts/generate-models.ts`, or thinking policies in `model-thinking.ts`.
@@ -0,0 +1,30 @@
1
+ import type { FetchImpl, ModelSpec } from "../types";
2
+ type GitLabDuoWorkflowCandidateSource = "override" | "project" | "remote" | "group";
3
+ export interface GitLabDuoWorkflowModelRef {
4
+ name: string;
5
+ ref: string;
6
+ }
7
+ /**
8
+ * GitLab Duo Workflow model/namespace discovery configuration.
9
+ */
10
+ export interface GitLabDuoWorkflowDiscoveryConfig {
11
+ apiKey: string;
12
+ baseUrl?: string;
13
+ fetch?: FetchImpl;
14
+ namespaceId?: string;
15
+ projectId?: string;
16
+ projectPath?: string;
17
+ cwd?: string;
18
+ }
19
+ export interface GitLabDuoWorkflowNamespaceSelection {
20
+ rootNamespaceId: string;
21
+ namespacePath?: string;
22
+ projectPath?: string;
23
+ source: GitLabDuoWorkflowCandidateSource;
24
+ }
25
+ export declare function discoverGitLabDuoWorkflowNamespace(config: GitLabDuoWorkflowDiscoveryConfig): Promise<GitLabDuoWorkflowNamespaceSelection>;
26
+ export declare function discoverGitLabDuoWorkflowRuntimeNamespace(config: GitLabDuoWorkflowDiscoveryConfig): Promise<GitLabDuoWorkflowNamespaceSelection>;
27
+ export declare function fetchGitLabDuoWorkflowModels(config: GitLabDuoWorkflowDiscoveryConfig): Promise<readonly ModelSpec<"gitlab-duo-agent">[] | null>;
28
+ export declare function buildGitLabDuoWorkflowModelSpec(model: GitLabDuoWorkflowModelRef, baseUrl?: string, rootNamespaceId?: string): ModelSpec<"gitlab-duo-agent">;
29
+ export declare function buildGitLabDuoWorkflowFallbackModel(id?: string, name?: string, baseUrl?: string): ModelSpec<"gitlab-duo-agent">;
30
+ export {};
@@ -1,4 +1,5 @@
1
1
  export * from "./antigravity";
2
2
  export * from "./codex";
3
3
  export * from "./gemini";
4
+ export * from "./gitlab-duo-workflow";
4
5
  export * from "./openai-compatible";
@@ -12,5 +12,6 @@ export * from "./types";
12
12
  export * from "./utils";
13
13
  export * from "./variant-collapse";
14
14
  export * from "./wire/codex";
15
+ export * from "./wire/coreweave";
15
16
  export * from "./wire/gemini-headers";
16
17
  export * from "./wire/github-copilot";
@@ -50,7 +50,7 @@ export declare function allowsUnauthenticatedCatalogDiscovery(descriptor: Catalo
50
50
  * - Every entry is a member of `KnownProvider`.
51
51
  * - `createModelManagerOptions` present (and not `specialModelManager`) ⇒
52
52
  * appears in `PROVIDER_DESCRIPTORS` for runtime model discovery.
53
- * - `catalogDiscovery` present ⇒ participates in `generate-models.ts`.
53
+ * - \`catalogDiscovery\` present ⇒ participates in \`generate-models.ts\`.
54
54
  */
55
55
  export interface ProviderCatalogEntry {
56
56
  readonly id: string;
@@ -101,6 +101,12 @@ export declare const CATALOG_PROVIDERS: readonly [{
101
101
  readonly id: "gitlab-duo";
102
102
  readonly defaultModel: "duo-chat-opus-4-6";
103
103
  readonly envVars: readonly ["GITLAB_TOKEN"];
104
+ }, {
105
+ readonly id: "gitlab-duo-agent";
106
+ readonly defaultModel: "claude_sonnet_4_6_vertex";
107
+ readonly envVars: readonly ["GITLAB_TOKEN"];
108
+ readonly createModelManagerOptions: (config: ModelManagerConfig) => import("..").ModelManagerOptions<"gitlab-duo-agent", unknown>;
109
+ readonly dynamicModelsAuthoritative: true;
104
110
  }, {
105
111
  readonly id: "google";
106
112
  readonly defaultModel: "gemini-3.1-pro-preview";
@@ -339,6 +345,14 @@ export declare const CATALOG_PROVIDERS: readonly [{
339
345
  readonly label: "Wafer Serverless";
340
346
  readonly oauthProvider: "wafer-serverless";
341
347
  };
348
+ }, {
349
+ readonly id: "coreweave";
350
+ readonly defaultModel: "openai/gpt-oss-120b";
351
+ readonly envVars: readonly ["COREWEAVE_API_KEY", "WANDB_API_KEY"];
352
+ readonly createModelManagerOptions: (config: ModelManagerConfig) => import("..").ModelManagerOptions<"openai-completions", unknown>;
353
+ readonly catalogDiscovery: {
354
+ readonly label: "CoreWeave Serverless Inference";
355
+ };
342
356
  }, {
343
357
  readonly id: "xai";
344
358
  readonly defaultModel: "grok-4-fast-non-reasoning";
@@ -34,10 +34,12 @@ export interface ModelsDevModel {
34
34
  * by the generator's policy pass (scripts/generated-policies.ts).
35
35
  */
36
36
  export declare const ANTHROPIC_CURATED_FALLBACK_MODELS: readonly ModelSpec<"anthropic-messages">[];
37
+ type SimpleProviderDiscoveryHeaders = Record<string, string> | (() => Record<string, string> | undefined);
37
38
  type SimpleProviderConfig = {
38
39
  apiKey?: string;
39
40
  baseUrl?: string;
40
41
  fetch?: FetchImpl;
42
+ headers?: SimpleProviderDiscoveryHeaders;
41
43
  };
42
44
  export declare function createSimpleOpenAICompletionsOptions(providerId: Parameters<typeof getBundledModels>[0], defaultBaseUrl: string, config?: SimpleProviderConfig): ModelManagerOptions<"openai-completions">;
43
45
  export interface UmansModelManagerConfig {
@@ -116,7 +118,7 @@ export declare const XAI_OAUTH_CURATED_MODELS: readonly XAICuratedModel[];
116
118
  * Single source of truth for the curated to Model fan-in, consumed by both
117
119
  * - {@link xaiOAuthModelManagerOptions} (runtime static seed handed to the model
118
120
  * manager so the picker is populated on a fresh login), and
119
- * - `packages/ai/scripts/generate-models.ts` (bundles the same entries into
121
+ * - \`packages/catalog/scripts/generate-models.ts\` (bundles the same entries into
120
122
  * `models.json`, so the synchronous `ModelRegistry.#loadModels()` boot path
121
123
  * sees `xai-oauth` without waiting for a refresh — fixes the boot-time
122
124
  * default-model reset when `modelRoles.default = "xai-oauth/<id>"`).
@@ -307,6 +309,12 @@ export interface TogetherModelManagerConfig {
307
309
  fetch?: FetchImpl;
308
310
  }
309
311
  export declare function togetherModelManagerOptions(config?: TogetherModelManagerConfig): ModelManagerOptions<"openai-completions">;
312
+ export interface CoreWeaveModelManagerConfig {
313
+ apiKey?: string;
314
+ baseUrl?: string;
315
+ fetch?: FetchImpl;
316
+ }
317
+ export declare function coreWeaveModelManagerOptions(config?: CoreWeaveModelManagerConfig): ModelManagerOptions<"openai-completions">;
310
318
  export interface MoonshotModelManagerConfig {
311
319
  apiKey?: string;
312
320
  baseUrl?: string;
@@ -355,6 +363,20 @@ export interface LiteLLMModelManagerConfig {
355
363
  baseUrl?: string;
356
364
  fetch?: FetchImpl;
357
365
  }
366
+ export interface FetchLiteLLMRichModelsOptions<TApi extends Api> {
367
+ api: TApi;
368
+ provider: Provider;
369
+ baseUrl: string;
370
+ apiKey?: string;
371
+ headers?: Record<string, string>;
372
+ fetch?: FetchImpl;
373
+ signal?: AbortSignal;
374
+ referenceResolver?: (modelId: string) => ModelSpec<TApi> | undefined;
375
+ }
376
+ export declare const OPENAI_COMPAT_DISCOVERY_DEFAULT_CONTEXT_WINDOW = 128000;
377
+ export declare const OPENAI_COMPAT_DISCOVERY_DEFAULT_MAX_TOKENS = 32768;
378
+ export declare function normalizeLiteLLMManagementBaseUrl(baseUrl: string): string;
379
+ export declare function fetchLiteLLMRichModels<TApi extends Api>(options: FetchLiteLLMRichModelsOptions<TApi>): Promise<ModelSpec<TApi>[] | null>;
358
380
  export declare function litellmModelManagerOptions(config?: LiteLLMModelManagerConfig): ModelManagerOptions<"openai-completions">;
359
381
  export interface VllmModelManagerConfig {
360
382
  apiKey?: string;
@@ -1,5 +1,6 @@
1
1
  import type { DevinModelDiscoveryOptions } from "../discovery/devin";
2
2
  import type { ModelManagerOptions } from "../model-manager";
3
+ import type { FetchImpl } from "../types";
3
4
  export interface OpenAICodexModelManagerConfig {
4
5
  accessToken?: string;
5
6
  accountId?: string;
@@ -12,6 +13,15 @@ export interface CursorModelManagerConfig {
12
13
  clientVersion?: string;
13
14
  }
14
15
  export declare function cursorModelManagerOptions(config?: CursorModelManagerConfig): ModelManagerOptions<"cursor-agent">;
16
+ export interface GitLabDuoWorkflowModelManagerConfig {
17
+ apiKey?: string;
18
+ baseUrl?: string;
19
+ fetch?: FetchImpl;
20
+ namespaceId?: string;
21
+ projectId?: string;
22
+ cwd?: string;
23
+ }
24
+ export declare function gitLabDuoWorkflowModelManagerOptions(config?: GitLabDuoWorkflowModelManagerConfig): ModelManagerOptions<"gitlab-duo-agent">;
15
25
  export interface DevinModelManagerConfig {
16
26
  apiKey?: string;
17
27
  baseUrl?: string;
@@ -1,6 +1,6 @@
1
1
  import type { Effort } from "./effort";
2
2
  export type { KnownProvider } from "./provider-models/descriptors";
3
- export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "devin-agent";
3
+ export type KnownApi = "openai-completions" | "openai-responses" | "openrouter" | "openai-codex-responses" | "azure-openai-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-gemini-cli" | "google-vertex" | "ollama-chat" | "cursor-agent" | "gitlab-duo-agent" | "devin-agent";
4
4
  export type Api = KnownApi | (string & {});
5
5
  /** Canonical thinking transport used by a model. */
6
6
  export type ThinkingControlMode = "effort" | "budget" | "google-level" | "anthropic-adaptive" | "anthropic-budget-effort";
@@ -199,6 +199,28 @@ export interface OpenAICompat {
199
199
  * models).
200
200
  */
201
201
  replayReasoningContent?: boolean;
202
+ /**
203
+ * Send `preserve_thinking: true` so the Qwen3.6+ chat template renders
204
+ * `<think>...</think>` markup for EVERY assistant turn (not just turns
205
+ * after the last user message). Without it, the template strips the think
206
+ * block from older assistant turns:
207
+ *
208
+ * ```jinja
209
+ * {%- if (preserve_thinking is defined and preserve_thinking is true)
210
+ * or (loop.index0 > ns.last_query_index) %}
211
+ * <|im_start|>assistant\n<think>\n{rc}\n</think>\n\n{content}
212
+ * {%- else %}
213
+ * <|im_start|>assistant\n{content}
214
+ * ```
215
+ *
216
+ * The cache from the original generation has `<think>...</think>` tokens,
217
+ * so once a new user message arrives the prior assistant turns become
218
+ * "older" and the stripped re-render diverges — full prompt re-processing
219
+ * on SWA models (#3541). Default: auto-detected (Qwen thinking format on
220
+ * a local llama.cpp-style backend, paired with `replayReasoningContent`).
221
+ * Non-Qwen templates ignore the flag, so the auto-detection is safe.
222
+ */
223
+ qwenPreserveThinking?: boolean;
202
224
  /** Whether assistant tool-call messages must include non-empty content. Default: false. */
203
225
  requiresAssistantContentForToolCalls?: boolean;
204
226
  /** Whether the provider supports the `tool_choice` parameter. Default: true. */
@@ -209,6 +231,14 @@ export interface OpenAICompat {
209
231
  * to provider-default auto selection. Default: true.
210
232
  */
211
233
  supportsForcedToolChoice?: boolean;
234
+ /**
235
+ * Whether the chat-completions endpoint accepts the object form that pins one
236
+ * named function (`{ type: "function", function: { name } }`). Some
237
+ * OpenAI-compatible hosts such as llama.cpp only accept string
238
+ * `tool_choice` values; request builders downgrade a named force to
239
+ * `"required"` when this is false. Default: true.
240
+ */
241
+ supportsNamedToolChoice?: boolean;
212
242
  /**
213
243
  * Drop reasoning fields (`reasoning_effort`, OpenRouter `reasoning`) for
214
244
  * the request when `tool_choice` forces a tool call. Mirrors the Anthropic
@@ -401,11 +431,13 @@ export interface ResolvedOpenAISharedCompat {
401
431
  disableReasoningOnToolChoice: boolean;
402
432
  supportsToolChoice: boolean;
403
433
  supportsForcedToolChoice: boolean;
434
+ supportsNamedToolChoice: boolean;
404
435
  reasoningContentField?: OpenAICompat["reasoningContentField"];
405
436
  requiresReasoningContentForToolCalls: boolean;
406
437
  requiresReasoningContentForAllAssistantTurns: boolean;
407
438
  allowsSyntheticReasoningContentForToolCalls: boolean;
408
439
  replayReasoningContent: boolean;
440
+ qwenPreserveThinking: boolean;
409
441
  requiresThinkingAsText: boolean;
410
442
  requiresMistralToolIds: boolean;
411
443
  requiresToolResultName: boolean;
@@ -433,7 +465,7 @@ export interface ResolvedOpenAISharedCompat {
433
465
  * `buildModel`; request handlers read fields and never detect, resolve, or
434
466
  * allocate.
435
467
  */
436
- export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "thinkingFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "requiresJuiceZeroHack" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
468
+ export type ResolvedOpenAICompat = ResolvedOpenAISharedCompat & Required<Omit<OpenAICompat, "supportsDeveloperRole" | "supportsReasoningEffort" | "reasoningEffortMap" | "supportsReasoningParams" | "thinkingFormat" | "reasoningDisableMode" | "omitReasoningEffort" | "includeEncryptedReasoning" | "filterReasoningHistory" | "disableReasoningOnForcedToolChoice" | "disableReasoningOnToolChoice" | "supportsToolChoice" | "supportsForcedToolChoice" | "supportsNamedToolChoice" | "reasoningContentField" | "requiresReasoningContentForToolCalls" | "requiresReasoningContentForAllAssistantTurns" | "allowsSyntheticReasoningContentForToolCalls" | "replayReasoningContent" | "qwenPreserveThinking" | "requiresThinkingAsText" | "requiresMistralToolIds" | "requiresToolResultName" | "requiresAssistantAfterToolResult" | "requiresAssistantContentForToolCalls" | "stripDeepseekSpecialTokens" | "streamMarkupHealingPattern" | "reasoningDeltasMayBeCumulative" | "emptyLengthFinishIsContextError" | "usesOpenAIToolCallIdLimit" | "promptCacheSessionHeader" | "openRouterRouting" | "isOpenRouterHost" | "supportsStrictMode" | "supportsLongPromptCacheRetention" | "alwaysSendMaxTokens" | "wireModelIdMode" | "vercelGatewayRouting" | "extraBody" | "toolStrictMode" | "toolSchemaFlavor" | "streamIdleTimeoutMs" | "cacheControlFormat" | "thinkingKeep" | "strictResponsesPairing" | "supportsImageDetailOriginal" | "requiresJuiceZeroHack" | "enableGeminiThinkingLoopGuard" | "whenThinking">> & {
437
469
  vercelGatewayRouting?: OpenAICompat["vercelGatewayRouting"];
438
470
  extraBody?: OpenAICompat["extraBody"];
439
471
  cacheControlFormat?: OpenAICompat["cacheControlFormat"];
@@ -494,6 +526,17 @@ export type ResolvedDevinCompat = Required<DevinCompat>;
494
526
  export type CompatConfigOf<TApi extends Api> = TApi extends "openai-completions" | "openrouter" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? OpenAICompat : TApi extends "anthropic-messages" ? AnthropicCompat : TApi extends "devin-agent" ? DevinCompat : undefined;
495
527
  /** Resolved compat for a given API: complete record, materialized once by `buildModel`. */
496
528
  export type CompatOf<TApi extends Api> = TApi extends "openrouter" ? ResolvedOpenRouterCompat : TApi extends "openai-completions" ? ResolvedOpenAICompat : TApi extends "openai-responses" | "azure-openai-responses" | "openai-codex-responses" ? ResolvedOpenAIResponsesCompat : TApi extends "anthropic-messages" ? ResolvedAnthropicCompat : TApi extends "devin-agent" ? ResolvedDevinCompat : undefined;
529
+ /** Provider-native compaction endpoint configuration for one model. */
530
+ export interface RemoteCompactionConfig<TApi extends Api = Api> {
531
+ /** Enables provider-native compaction for providers not enabled by built-in policy. */
532
+ enabled?: boolean;
533
+ /** Adapter family used by the configured compaction endpoint. */
534
+ api?: TApi;
535
+ /** Absolute compact endpoint URL; when omitted, the adapter derives it from the model base URL. */
536
+ endpoint?: string;
537
+ /** Model id sent to the compaction endpoint when it differs from the active model id. */
538
+ model?: string;
539
+ }
497
540
  export interface Model<TApi extends Api = Api> {
498
541
  id: string;
499
542
  /**
@@ -523,6 +566,8 @@ export interface Model<TApi extends Api = Api> {
523
566
  * reports that native tool calling is unsupported.
524
567
  */
525
568
  supportsTools?: boolean;
569
+ /** GitLab Duo Workflow root namespace selected during catalog discovery. */
570
+ gitlabDuoWorkflowRootNamespaceId?: string;
526
571
  cost: {
527
572
  input: number;
528
573
  output: number;
@@ -565,6 +610,10 @@ export interface Model<TApi extends Api = Api> {
565
610
  preferWebsockets?: boolean;
566
611
  /** Preferred model to switch to when context promotion is triggered (model id or provider/id). */
567
612
  contextPromotionTarget?: string;
613
+ /** Preferred model to use only for compaction (model id or provider/id); the active session model is unchanged. */
614
+ compactionModel?: string;
615
+ /** Provider-native compaction endpoint configuration. */
616
+ remoteCompaction?: RemoteCompactionConfig<TApi>;
568
617
  /** Provider-assigned priority value (lower = higher priority). */
569
618
  priority?: number;
570
619
  /** Canonical thinking capability metadata for this model. */
@@ -0,0 +1,11 @@
1
+ export declare const COREWEAVE_PROJECT_HEADER: "OpenAI-Project";
2
+ export interface CoreWeaveProjectEnv {
3
+ [key: string]: string | undefined;
4
+ COREWEAVE_PROJECT?: string;
5
+ WANDB_INFERENCE_PROJECT?: string;
6
+ WANDB_ENTITY?: string;
7
+ WANDB_PROJECT?: string;
8
+ }
9
+ export declare function resolveCoreWeaveProject(env: CoreWeaveProjectEnv): string | undefined;
10
+ export declare function coreWeaveProjectHeaders(env: CoreWeaveProjectEnv): Record<string, string> | undefined;
11
+ export declare function hasCoreWeaveProjectHeader(headers: Record<string, string>): boolean;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@oh-my-pi/pi-catalog",
4
- "version": "16.1.22",
4
+ "version": "16.2.0",
5
5
  "description": "Model catalog for omp: bundled model database, provider discovery descriptors, model identity, classification, and equivalence",
6
6
  "homepage": "https://omp.sh",
7
7
  "author": "Can Boluk",
@@ -30,16 +30,16 @@
30
30
  "test": "bun test --parallel",
31
31
  "fix": "biome check --write --unsafe .",
32
32
  "fmt": "biome format --write .",
33
- "generate-models": "bun scripts/generate-models.ts"
33
+ "gen:models": "bun scripts/generate-models.ts"
34
34
  },
35
35
  "dependencies": {
36
36
  "@bufbuild/protobuf": "^2.12.0",
37
- "@oh-my-pi/pi-utils": "16.1.22",
37
+ "@oh-my-pi/pi-utils": "16.2.0",
38
38
  "arktype": "^2.2.0",
39
39
  "zod": "^4"
40
40
  },
41
41
  "devDependencies": {
42
- "@oh-my-pi/pi-ai": "16.1.22",
42
+ "@oh-my-pi/pi-ai": "16.2.0",
43
43
  "@types/bun": "^1.3.14"
44
44
  },
45
45
  "engines": {
@@ -403,6 +403,7 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
403
403
  disableReasoningOnToolChoice: isDeepseekFamily && Boolean(spec.reasoning) && !isOpenRouter,
404
404
  supportsToolChoice: !isDirectDeepseekReasoning,
405
405
  supportsForcedToolChoice: true,
406
+ supportsNamedToolChoice: provider !== "llama.cpp",
406
407
  maxTokensField: useMaxTokens ? "max_tokens" : "max_completion_tokens",
407
408
  requiresToolResultName: isMistral,
408
409
  requiresAssistantAfterToolResult: isMistral,
@@ -468,6 +469,20 @@ export function buildOpenAICompat(spec: ModelSpec<"openai-completions">): Resolv
468
469
  replayReasoningContent:
469
470
  !PROXY_OPENAI_COMPAT_PROVIDERS.has(provider) &&
470
471
  (LOCAL_OPENAI_COMPAT_PROVIDERS.has(provider) || hasLocalLoopbackBaseUrl(baseUrl)),
472
+ // `preserve_thinking: true` makes the Qwen3.6+ chat template render
473
+ // `<think>...</think>` for older assistant turns too, instead of
474
+ // stripping it the moment a new user message moves them past
475
+ // `last_query_index`. Without it, the slot's KV cache (which holds the
476
+ // raw `<think>X</think>` tokens emitted during generation) diverges
477
+ // from the next-turn render and llama.cpp falls back to full prompt
478
+ // re-processing — the exact symptom reported in #3541. Auto-enabled
479
+ // for Qwen thinking dialects on local llama.cpp-style backends (paired
480
+ // with `replayReasoningContent` above). Non-Qwen templates ignore the
481
+ // parameter, so the flag stays a no-op outside the Qwen path.
482
+ qwenPreserveThinking:
483
+ (thinkingFormat === "qwen" || thinkingFormat === "qwen-chat-template") &&
484
+ !PROXY_OPENAI_COMPAT_PROVIDERS.has(provider) &&
485
+ (LOCAL_OPENAI_COMPAT_PROVIDERS.has(provider) || hasLocalLoopbackBaseUrl(baseUrl)),
471
486
  requiresAssistantContentForToolCalls: isKimiModel || isDirectDeepseekReasoning,
472
487
  cacheControlFormat: isOpenRouter && spec.id.startsWith("anthropic/") ? "anthropic" : undefined,
473
488
  openRouterRouting: undefined,
@@ -579,6 +594,7 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
579
594
  disableReasoningOnToolChoice: isDeepseekFamily && reasoningCapable && !isOpenRouter,
580
595
  supportsToolChoice: true,
581
596
  supportsForcedToolChoice: true,
597
+ supportsNamedToolChoice: true,
582
598
  reasoningContentField: "reasoning_content",
583
599
  requiresReasoningContentForToolCalls:
584
600
  (isKimiModel || (isDeepseekFamily && reasoningCapable) || (isOpenRouter && reasoningCapable)) &&
@@ -589,6 +605,9 @@ export function buildOpenAIResponsesCompat(spec: OpenAIResponsesSpecLike): Resol
589
605
  // not via a top-level `reasoning_content` field — this flag is
590
606
  // chat-completions-only.
591
607
  replayReasoningContent: false,
608
+ // Responses-only; the Qwen `preserve_thinking` template knob lives on
609
+ // the chat-completions wire shape, never on Responses.
610
+ qwenPreserveThinking: false,
592
611
  requiresThinkingAsText: false,
593
612
  requiresMistralToolIds: false,
594
613
  requiresToolResultName: false,