1import {
2 type ModelCapabilities,
3 type ProviderOptionChoice,
4 type ProviderOptionDescriptor,
5} from "@t3tools/contracts";
6import { createModelCapabilities } from "@t3tools/shared/model";
7import * as Predicate from "effect/Predicate";
8
9// xhigh/max require per-model thinkingLevelMap opt-in; global advertising makes set_thinking_level fail.
10const PI_THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
11
12export type PiThinkingLevel = (typeof PI_THINKING_LEVELS)[number];
13
14const PI_THINKING_LEVEL_LABELS: Record<PiThinkingLevel, string> = {
15 off: "Off",
16 minimal: "Minimal",
17 low: "Low",
18 medium: "Medium",
19 high: "High",
20 xhigh: "Extra High",
21 max: "Max",
22};
23
24export const EMPTY_PI_MODEL_CAPABILITIES: ModelCapabilities = createModelCapabilities({
25 optionDescriptors: [],
26});
27
28/** Selecting Standard sends no `service_tier`, leaving the endpoint's default. */
29export const PI_STANDARD_SERVICE_TIER = "default";
30
31export function piModelCapabilities(
32 model: unknown,
33 defaultThinkingLevel: unknown,
34): ModelCapabilities {
35 const optionDescriptors = [
36 thinkingDescriptor(model, defaultThinkingLevel),
37 serviceTierDescriptor(model),
38 ].filter(Predicate.isNotUndefined);
39 return optionDescriptors.length === 0
40 ? EMPTY_PI_MODEL_CAPABILITIES
41 : createModelCapabilities({ optionDescriptors });
42}
43
44function thinkingDescriptor(
45 model: unknown,
46 defaultThinkingLevel: unknown,
47): ProviderOptionDescriptor | undefined {
48 const levels = supportedPiThinkingLevelsFromModel(model);
49 if (levels.length === 0) return undefined;
50 const defaultLevel = clampPiThinkingLevel(defaultThinkingLevel, levels);
51 return {
52 id: "thinking",
53 label: "Thinking",
54 type: "select",
55 options: levels.map((level): ProviderOptionChoice => ({
56 id: level,
57 label: PI_THINKING_LEVEL_LABELS[level],
58 ...(level === defaultLevel ? { isDefault: true } : {}),
59 })),
60 };
61}
62
63// Pi has no service tier concept; provider extensions advertise `serviceTiers` on a model,
64// and T3's injected extension sends the chosen id as the request's `service_tier`.
65function serviceTierDescriptor(model: unknown): ProviderOptionDescriptor | undefined {
66 const tiers = recordField(model, "serviceTiers");
67 if (!Array.isArray(tiers)) return undefined;
68 const seen = new Set<string>([PI_STANDARD_SERVICE_TIER]);
69 const options: Array<ProviderOptionChoice> = [];
70 for (const tier of tiers) {
71 const id = recordField(tier, "id");
72 if (typeof id !== "string" || id.trim() === "" || seen.has(id)) continue;
73 seen.add(id);
74 const name = recordField(tier, "name");
75 const description = recordField(tier, "description");
76 options.push({
77 id,
78 label: typeof name === "string" && name.trim() !== "" ? name : id,
79 ...(typeof description === "string" && description.trim() !== "" ? { description } : {}),
80 });
81 }
82 if (options.length === 0) return undefined;
83 return {
84 id: "serviceTier",
85 label: "Service Tier",
86 type: "select",
87 options: [{ id: PI_STANDARD_SERVICE_TIER, label: "Standard", isDefault: true }, ...options],
88 currentValue: PI_STANDARD_SERVICE_TIER,
89 };
90}
91
92function clampPiThinkingLevel(
93 input: unknown,
94 availableLevels: ReadonlyArray<PiThinkingLevel>,
95): PiThinkingLevel | undefined {
96 if (typeof input !== "string") return undefined;
97 const requestedIndex = PI_THINKING_LEVELS.findIndex((level) => level === input);
98 if (requestedIndex === -1) return undefined;
99 const exact = availableLevels.find((level) => level === input);
100 if (exact !== undefined) return exact;
101 for (let index = requestedIndex + 1; index < PI_THINKING_LEVELS.length; index += 1) {
102 const higher = availableLevels.find((level) => level === PI_THINKING_LEVELS[index]);
103 if (higher !== undefined) return higher;
104 }
105 for (let index = requestedIndex - 1; index >= 0; index -= 1) {
106 const lower = availableLevels.find((level) => level === PI_THINKING_LEVELS[index]);
107 if (lower !== undefined) return lower;
108 }
109 return availableLevels[0];
110}
111
112function supportedPiThinkingLevelsFromModel(model: unknown): ReadonlyArray<PiThinkingLevel> {
113 if (recordField(model, "reasoning") !== true) return [];
114 const thinkingLevelMap = thinkingLevelMapFromModel(model);
115 return PI_THINKING_LEVELS.filter((level) => {
116 const mapped = thinkingLevelMap?.[level];
117 if (mapped === null) return false;
118 if (level === "xhigh" || level === "max") return mapped !== undefined;
119 return true;
120 });
121}
122
123function thinkingLevelMapFromModel(model: unknown): Record<string, unknown> | undefined {
124 const value = recordField(model, "thinkingLevelMap");
125 if (typeof value !== "object" || value === null || Array.isArray(value)) return undefined;
126 return value as Record<string, unknown>;
127}
128
129function recordField(input: unknown, key: string): unknown {
130 return Predicate.isObject(input) ? input[key] : undefined;
131}