Documentation
¶
Overview ¶
Package util provides utility functions for the switchAILocal server.
Package util provides utility functions used across the switchAILocal application. These functions handle common tasks such as determining AI service providers from model names and managing HTTP proxies.
Package util provides utility functions for the switchAILocal server. It includes helper functions for proxy configuration, HTTP client setup, log level management, and other common operations used across the application.
Package util provides helper functions for SSH tunnel instructions and network-related tasks. This includes detecting the appropriate IP address and printing commands to help users connect to the local server from a remote machine.
Package util provides utility functions for the switchAILocal server.
Package util provides utility functions for the switchAILocal server. It includes helper functions for JSON manipulation, proxy configuration, and other common operations used across the application.
Package util provides utility functions for the switchAILocal server. It includes helper functions for logging configuration, file system operations, and other common utilities used throughout the application.
Index ¶
- Constants
- Variables
- func ApplyClaudeThinkingConfig(body []byte, budget *int) []byte
- func ApplyCustomHeadersFromAttrs(r *http.Request, attrs map[string]string)
- func ApplyDefaultThinkingIfNeeded(model string, body []byte) []byte
- func ApplyDefaultThinkingIfNeededCLI(model string, body []byte) []byte
- func ApplyGemini3ThinkingLevelFromMetadata(model string, metadata map[string]any, body []byte) []byte
- func ApplyGemini3ThinkingLevelFromMetadataCLI(model string, metadata map[string]any, body []byte) []byte
- func ApplyGeminiCLIThinkingConfig(body []byte, budget *int, includeThoughts *bool) []byte
- func ApplyGeminiCLIThinkingLevel(body []byte, level string, includeThoughts *bool) []byte
- func ApplyGeminiThinkingConfig(body []byte, budget *int, includeThoughts *bool) []byte
- func ApplyGeminiThinkingLevel(body []byte, level string, includeThoughts *bool) []byte
- func ApplyReasoningEffortToGemini(body []byte, effort string) []byte
- func ApplyReasoningEffortToGeminiCLI(body []byte, effort string) []byte
- func CleanJSONSchemaForAntigravity(jsonStr string) string
- func ConvertThinkingLevelToBudget(body []byte, model string, skipGemini3Check ...bool) []byte
- func ConvertThinkingLevelToBudgetCLI(body []byte, model string) []byte
- func CountAuthFiles(authDir string) int
- func CreateWhiteImageBase64(aspectRatio string) (string, error)
- func DeleteKey(jsonStr, keyName string) string
- func ExpandPath(path string) (string, error)
- func ExtractThinkTags(content string) (thinking string, cleaned string, found bool)
- func FixJSON(input string) string
- func GetIPAddress() string
- func GetLoginHint(provider string) string
- func GetModelThinkingLevels(model string) []string
- func GetOpenAICompatibilityConfig(alias string, cfg *config.Config) (*config.OpenAICompatibility, *config.OpenAICompatibilityModel)
- func GetProviderName(modelName string) []string
- func GetThinkingText(part gjson.Result) string
- func GetThinkingTextFromJSON(jsonStr string) string
- func HardenPermissions(sb *StateBox) error
- func HideAPIKey(apiKey string) string
- func InArray(hystack []string, needle string) bool
- func IsClaudeThinkingModel(model string) bool
- func IsGemini3FlashModel(model string) bool
- func IsGemini3Model(model string) bool
- func IsGemini3ProModel(model string) bool
- func IsGemini25Model(model string) bool
- func IsOpenAICompatibilityAlias(modelName string, cfg *config.Config) bool
- func IsOpenAICompatibilityModel(model string) bool
- func IsValidPluginID(id string) bool
- func LuaDateFormatToGo(luaFormat string) string
- func MaskAuthorizationHeader(value string) string
- func MaskSensitiveHeaderValue(key, value string) string
- func MaskSensitiveJSONBody(body []byte) []byte
- func MaskSensitiveQuery(raw string) string
- func ModelHasDefaultThinking(model string) bool
- func ModelSupportsThinking(model string) bool
- func ModelUsesThinkingLevels(model string) bool
- func NormalizeGeminiCLIThinkingBudget(model string, body []byte, skipGemini3Check ...bool) []byte
- func NormalizeGeminiThinkingBudget(model string, body []byte, skipGemini3Check ...bool) []byte
- func NormalizeReasoningEffortLevel(model, effort string) (string, bool)
- func NormalizeThinkingBudget(model string, budget int) int
- func NormalizeThinkingModel(modelName string) (string, map[string]any)
- func ParseProviderPrefix(modelName string) (provider, model string)
- func PrintSSHTunnelInstructions(port int)
- func ReasoningEffortFromMetadata(metadata map[string]any) (string, bool)
- func RenameKey(jsonStr, oldKeyPath, newKeyPath string) (string, error)
- func ResolveAuthDir(authDir string) (string, error)
- func ResolveAutoModel(modelName string, priorityList []string) string
- func ResolveClaudeThinkingConfig(modelName string, metadata map[string]any) (*int, bool)
- func ResolveOriginalModel(model string, metadata map[string]any) string
- func ResolveThinkingConfigFromMetadata(model string, metadata map[string]any) (*int, *bool, bool)
- func SanitizeThinkingPart(part gjson.Result) string
- func SecureWrite(sb *StateBox, path string, data []byte, opts *SecureWriteOptions) error
- func SecureWriteJSON(sb *StateBox, path string, v interface{}, opts *SecureWriteOptions) error
- func SetLogLevel(cfg *config.Config)
- func SetProxy(cfg *config.SDKConfig, httpClient *http.Client) *http.Client
- func StripCacheControl(jsonStr string) string
- func StripThinkingConfigIfUnsupported(model string, body []byte) []byte
- func ThinkingBudgetToEffort(model string, budget int) (string, bool)
- func ThinkingBudgetToGemini3Level(model string, budget int) (string, bool)
- func ThinkingEffortToBudget(model, effort string) (int, bool)
- func ThinkingFromMetadata(metadata map[string]any) (*int, *bool, *string, bool)
- func ThinkingLevelToBudget(level string) (int, bool)
- func ValidateGemini3ThinkingLevel(model, level string) (string, bool)
- func ValidateProviderPrefix(providerPrefix, model string) (string, error)
- func Walk(value gjson.Result, path, field string, paths *[]string)
- func WritablePath() string
- type AuditResult
- type ProviderError
- type ProviderErrorType
- type SecureWriteOptions
- type StateBox
- func (sb *StateBox) CredentialPath(provider string) string
- func (sb *StateBox) CredentialsDir() string
- func (sb *StateBox) DiscoveryDir() string
- func (sb *StateBox) EnsureCredentialsDir() (string, error)
- func (sb *StateBox) EnsureDir(path string) error
- func (sb *StateBox) HasLegacyAuthDir() bool
- func (sb *StateBox) IntelligenceDir() string
- func (sb *StateBox) IsReadOnly() bool
- func (sb *StateBox) ReadCredential(provider string, v interface{}) error
- func (sb *StateBox) ResolvePath(relativePath string) string
- func (sb *StateBox) RootPath() string
- func (sb *StateBox) SetLegacyAuthDir(authDir string) error
- func (sb *StateBox) WriteCredential(provider string, v interface{}) error
Constants ¶
const ( GeminiThinkingBudgetMetadataKey = "gemini_thinking_budget" GeminiIncludeThoughtsMetadataKey = "gemini_include_thoughts" GeminiOriginalModelMetadataKey = "gemini_original_model" )
const ( ThinkingBudgetMetadataKey = "thinking_budget" ThinkingIncludeThoughtsMetadataKey = "thinking_include_thoughts" ReasoningEffortMetadataKey = "reasoning_effort" ThinkingOriginalModelMetadataKey = "thinking_original_model" )
Variables ¶
var ErrReadOnlyMode = errors.New("Read-only environment: write operations disabled")
ErrReadOnlyMode is returned when a write operation is attempted in read-only mode.
var Gemini3FlashThinkingLevels = []string{"minimal", "low", "medium", "high"}
Gemini3FlashThinkingLevels are the valid thinkingLevel values for Gemini 3 Flash models.
var Gemini3ProThinkingLevels = []string{"low", "high"}
Gemini3ProThinkingLevels are the valid thinkingLevel values for Gemini 3 Pro models.
var KnownProviders = map[string]string{ "switchai": "Traylinx switchAI", constant.Gemini: "Google Gemini", constant.GeminiCLI: "Gemini CLI", constant.Claude: "Anthropic Claude", constant.ClaudeCLI: "Claude CLI", constant.Codex: "OpenAI Codex", "ollama": "Ollama (Local)", constant.VibeCLI: "Vibe CLI", constant.OpenAI: "OpenAI", "openai-compat": "OpenAI Compatible", "opencode": "OpenCode Agent", }
KnownProviders contains all recognized provider prefixes.
var ReasoningEffortBudgetMapping = map[string]int{
"none": 0,
"auto": -1,
"minimal": 512,
"low": 1024,
"medium": 8192,
"high": 24576,
"xhigh": 32768,
}
ReasoningEffortBudgetMapping defines the thinkingBudget values for each reasoning effort level.
Functions ¶
func ApplyClaudeThinkingConfig ¶
ApplyClaudeThinkingConfig applies thinking configuration to a Claude API request payload. It sets the thinking.type to "enabled" and thinking.budget_tokens to the specified budget. If budget is nil or the payload already has thinking config, it returns the payload unchanged.
func ApplyCustomHeadersFromAttrs ¶
ApplyCustomHeadersFromAttrs applies user-defined headers stored in the provided attributes map. Custom headers override built-in defaults when conflicts occur.
func ApplyDefaultThinkingIfNeeded ¶
ApplyDefaultThinkingIfNeeded injects default thinkingConfig for models that require it. For standard Gemini API format (generationConfig.thinkingConfig path). Returns the modified body if thinkingConfig was added, otherwise returns the original. For Gemini 3 models, uses thinkingLevel instead of thinkingBudget per Google's documentation.
func ApplyDefaultThinkingIfNeededCLI ¶
ApplyDefaultThinkingIfNeededCLI injects default thinkingConfig for models that require it. For Gemini CLI API format (request.generationConfig.thinkingConfig path). Returns the modified body if thinkingConfig was added, otherwise returns the original. For Gemini 3 models, uses thinkingLevel instead of thinkingBudget per Google's documentation.
func ApplyGemini3ThinkingLevelFromMetadata ¶
func ApplyGemini3ThinkingLevelFromMetadata(model string, metadata map[string]any, body []byte) []byte
ApplyGemini3ThinkingLevelFromMetadata applies thinkingLevel from metadata for Gemini 3 models. For standard Gemini API format (generationConfig.thinkingConfig path). This handles the case where reasoning_effort is specified via model name suffix (e.g., model(minimal)).
func ApplyGemini3ThinkingLevelFromMetadataCLI ¶
func ApplyGemini3ThinkingLevelFromMetadataCLI(model string, metadata map[string]any, body []byte) []byte
ApplyGemini3ThinkingLevelFromMetadataCLI applies thinkingLevel from metadata for Gemini 3 models. For Gemini CLI API format (request.generationConfig.thinkingConfig path). This handles the case where reasoning_effort is specified via model name suffix (e.g., model(minimal)).
func ApplyGeminiCLIThinkingLevel ¶
ApplyGeminiCLIThinkingLevel applies thinkingLevel config for Gemini 3 models. For Gemini CLI API format (request.generationConfig.thinkingConfig path). Per Google's documentation, Gemini 3 models should use thinkingLevel instead of thinkingBudget.
func ApplyGeminiThinkingLevel ¶
ApplyGeminiThinkingLevel applies thinkingLevel config for Gemini 3 models. For standard Gemini API format (generationConfig.thinkingConfig path). Per Google's documentation, Gemini 3 models should use thinkingLevel instead of thinkingBudget.
func ApplyReasoningEffortToGemini ¶
ApplyReasoningEffortToGemini applies OpenAI reasoning_effort to Gemini thinkingConfig for standard Gemini API format (generationConfig.thinkingConfig path). Returns the modified body with thinkingBudget and include_thoughts set.
func ApplyReasoningEffortToGeminiCLI ¶
ApplyReasoningEffortToGeminiCLI applies OpenAI reasoning_effort to Gemini CLI thinkingConfig for Gemini CLI API format (request.generationConfig.thinkingConfig path). Returns the modified body with thinkingBudget and include_thoughts set.
func CleanJSONSchemaForAntigravity ¶
CleanJSONSchemaForAntigravity transforms a JSON schema to be compatible with Antigravity API. It handles unsupported keywords, type flattening, and schema simplification while preserving semantic information as description hints.
func ConvertThinkingLevelToBudget ¶
ConvertThinkingLevelToBudget checks for "generationConfig.thinkingConfig.thinkingLevel" and converts it to "thinkingBudget" for Gemini 2.5 models. For Gemini 3 models, preserves thinkingLevel unless skipGemini3Check is provided and true. Mappings for Gemini 2.5:
- "high" -> 32768
- "medium" -> 8192
- "low" -> 1024
- "minimal" -> 512
It removes "thinkingLevel" after conversion (for Gemini 2.5 only).
func ConvertThinkingLevelToBudgetCLI ¶
ConvertThinkingLevelToBudgetCLI checks for "request.generationConfig.thinkingConfig.thinkingLevel" and converts it to "thinkingBudget" for Gemini 2.5 models. For Gemini 3 models, preserves thinkingLevel as-is (does not convert).
func CountAuthFiles ¶
CountAuthFiles returns the number of JSON auth files located under the provided directory. The function resolves leading tildes to the user's home directory and performs a case-insensitive match on the ".json" suffix so that files saved with uppercase extensions are also counted.
func CreateWhiteImageBase64 ¶
func ExpandPath ¶
ExpandPath normalizes a path by expanding a leading tilde (~) to the user's home directory.
func ExtractThinkTags ¶
ExtractThinkTags detects <think>...</think> tags embedded in a content string and separates the thinking text from the clean content.
Many providers (MiniMax, DeepSeek R1, etc.) embed chain-of-thought reasoning inside <think> tags within the content field instead of using the OpenAI-standard reasoning_content field. This function normalizes that by splitting the content.
Returns:
- thinking: the text inside <think>...</think> tags (may span multiple blocks)
- cleaned: the content with all <think>...</think> blocks removed
- found: true if at least one <think> tag was found
Edge cases handled:
- No <think> tag: returns ("", original, false) — zero overhead path
- Unclosed <think> tag: treats everything after <think> as thinking
- Multiple <think> blocks: concatenates all thinking text
- Empty <think></think>: found=true but thinking=""
- Whitespace around tags and in content is preserved internally but leading/trailing whitespace on both outputs is trimmed
func FixJSON ¶
FixJSON converts non-standard JSON that uses single quotes for strings into RFC 8259-compliant JSON by converting those single-quoted strings to double-quoted strings with proper escaping.
Examples:
{'a': 1, 'b': '2'} => {"a": 1, "b": "2"}
{"t": 'He said "hi"'} => {"t": "He said \"hi\""}
Rules:
- Existing double-quoted JSON strings are preserved as-is.
- Single-quoted strings are converted to double-quoted strings.
- Inside converted strings, any double quote is escaped (\").
- Common backslash escapes (\n, \r, \t, \b, \f, \\) are preserved.
- \' inside single-quoted strings becomes a literal ' in the output (no escaping needed inside double quotes).
- Unicode escapes (\uXXXX) inside single-quoted strings are forwarded.
- The function does not attempt to fix other non-JSON features beyond quotes.
func GetIPAddress ¶
func GetIPAddress() string
GetIPAddress attempts to find the best-available IP address. It first tries to get the public IP address, and if that fails, it falls back to getting the local outbound IP address.
Returns:
- string: The determined IP address (preferring public IPv4)
func GetLoginHint ¶
GetLoginHint returns the recommended command-line flag or instruction to obtain credentials for the given provider.
func GetModelThinkingLevels ¶
GetModelThinkingLevels returns the discrete reasoning effort levels for the model. Returns nil if the model has no thinking support or no levels defined.
func GetOpenAICompatibilityConfig ¶
func GetOpenAICompatibilityConfig(alias string, cfg *config.Config) (*config.OpenAICompatibility, *config.OpenAICompatibilityModel)
GetOpenAICompatibilityConfig returns the OpenAI compatibility configuration and model details for the given alias.
Parameters:
- alias: The model alias to find configuration for
- cfg: The application configuration containing OpenAI compatibility settings
Returns:
- *config.OpenAICompatibility: The matching compatibility configuration, or nil if not found
- *config.OpenAICompatibilityModel: The matching model configuration, or nil if not found
func GetProviderName ¶
GetProviderName determines all AI service providers capable of serving a registered model. It first queries the global model registry to retrieve the providers backing the supplied model name. When the model has not been registered yet, it falls back to legacy string heuristics to infer potential providers.
Supported providers include (but are not limited to):
- "gemini" for Google's Gemini family
- "codex" for OpenAI GPT-compatible providers
- "claude" for Anthropic models
- "qwen" for Alibaba's Qwen models
- "openai-compatibility" for external OpenAI-compatible providers
Parameters:
- modelName: The name of the model to identify providers for.
- cfg: The application configuration containing OpenAI compatibility settings.
Returns:
- []string: All provider identifiers capable of serving the model, ordered by preference.
func GetThinkingText ¶
GetThinkingText extracts the thinking text from a content part. Handles various formats: - Simple string: { "thinking": "text" } or { "text": "text" } - Wrapped object: { "thinking": { "text": "text", "cache_control": {...} } } - Gemini-style: { "thought": true, "text": "text" } Returns the extracted text string.
func GetThinkingTextFromJSON ¶
GetThinkingTextFromJSON extracts thinking text from a raw JSON string.
func HardenPermissions ¶
HardenPermissions audits and corrects permissions in the State Box. Directories are set to 0700 (owner read/write/execute only). Sensitive files (.db, .json) are set to 0600 (owner read/write only). Errors are logged as warnings, but the function continues processing. Returns an error only if the State Box is nil or the directory walk fails completely.
func HideAPIKey ¶
HideAPIKey obscures an API key for logging purposes, showing only the first and last few characters.
Parameters:
- apiKey: The API key to hide.
Returns:
- string: The obscured API key.
func InArray ¶
InArray checks if a string exists in a slice of strings. It iterates through the slice and returns true if the target string is found, otherwise it returns false.
Parameters:
- hystack: The slice of strings to search in
- needle: The string to search for
Returns:
- bool: True if the string is found, false otherwise
func IsClaudeThinkingModel ¶
IsClaudeThinkingModel checks if the model is a Claude thinking model that requires the interleaved-thinking beta header.
func IsGemini3FlashModel ¶
IsGemini3FlashModel returns true if the model is a Gemini 3 Flash variant. Gemini 3 Flash supports thinkingLevel: "minimal", "low", "medium", "high" (default: "high")
func IsGemini3Model ¶
IsGemini3Model returns true if the model is a Gemini 3 family model. Gemini 3 models should use thinkingLevel (string) instead of thinkingBudget (number).
func IsGemini3ProModel ¶
IsGemini3ProModel returns true if the model is a Gemini 3 Pro variant. Gemini 3 Pro supports thinkingLevel: "low", "high" (default: "high")
func IsGemini25Model ¶
IsGemini25Model returns true if the model is a Gemini 2.5 family model. Gemini 2.5 models should use thinkingBudget (number).
func IsOpenAICompatibilityAlias ¶
IsOpenAICompatibilityAlias checks if the given model name is an alias configured for OpenAI compatibility routing.
Parameters:
- modelName: The model name to check
- cfg: The application configuration containing OpenAI compatibility settings
Returns:
- bool: True if the model name is an OpenAI compatibility alias, false otherwise
func IsOpenAICompatibilityModel ¶
IsOpenAICompatibilityModel reports whether the model is registered as an OpenAI-compatibility model. These models may not advertise Thinking metadata in the registry.
func IsValidPluginID ¶
IsValidPluginID checks if the plugin ID is a valid slug.
func LuaDateFormatToGo ¶
LuaDateFormatToGo converts a LUA date format string (strftime style) to a Go time.Format string (layout style). Note: This is an architectural mapping; for standard LUA plugins we support the most common strftime tokens used for basic logging and headers.
func MaskAuthorizationHeader ¶
maskAuthorizationHeader masks the Authorization header value while preserving the auth type prefix. Common formats: "Bearer <token>", "Basic <credentials>", "ApiKey <key>", etc. It preserves the prefix (e.g., "Bearer ") and only masks the token/credential part.
Parameters:
- value: The Authorization header value
Returns:
- string: The masked Authorization value with prefix preserved
func MaskSensitiveHeaderValue ¶
MaskSensitiveHeaderValue masks sensitive header values while preserving expected formats.
Behavior by header key (case-insensitive):
- "Authorization": Preserve the auth type prefix (e.g., "Bearer ") and mask only the credential part.
- Headers containing "api-key": Mask the entire value using HideAPIKey.
- Others: Return the original value unchanged.
Parameters:
- key: The HTTP header name to inspect (case-insensitive matching).
- value: The header value to mask when sensitive.
Returns:
- string: The masked value according to the header type; unchanged if not sensitive.
func MaskSensitiveJSONBody ¶
MaskSensitiveJSONBody masks sensitive fields in a JSON body. It uses regex to identify and mask values of sensitive keys while preserving the JSON structure.
func MaskSensitiveQuery ¶
MaskSensitiveQuery masks sensitive query parameters, e.g. auth_token, within the raw query string.
func ModelHasDefaultThinking ¶
ModelHasDefaultThinking returns true if the model should have thinking enabled by default.
func ModelSupportsThinking ¶
ModelSupportsThinking reports whether the given model has Thinking capability according to the model registry metadata (provider-agnostic).
func ModelUsesThinkingLevels ¶
ModelUsesThinkingLevels reports whether the model uses discrete reasoning effort levels instead of numeric budgets.
func NormalizeGeminiCLIThinkingBudget ¶
NormalizeGeminiCLIThinkingBudget normalizes the thinkingBudget value in a Gemini CLI request body (request.generationConfig.thinkingConfig.thinkingBudget path). For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation, unless skipGemini3Check is provided and true.
func NormalizeGeminiThinkingBudget ¶
NormalizeGeminiThinkingBudget normalizes the thinkingBudget value in a standard Gemini request body (generationConfig.thinkingConfig.thinkingBudget path). For Gemini 3 models, converts thinkingBudget to thinkingLevel per Google's documentation, unless skipGemini3Check is provided and true.
func NormalizeReasoningEffortLevel ¶
NormalizeReasoningEffortLevel validates and normalizes a reasoning effort level for the given model. Returns false when the level is not supported.
func NormalizeThinkingBudget ¶
NormalizeThinkingBudget clamps the requested thinking budget to the supported range for the specified model using registry metadata only. If the model is unknown or has no Thinking metadata, returns the original budget. For dynamic (-1), returns -1 if DynamicAllowed; otherwise approximates mid-range or min (0 if zero is allowed and mid <= 0).
func NormalizeThinkingModel ¶
NormalizeThinkingModel parses dynamic thinking suffixes on model names and returns the normalized base model with extracted metadata. Supported pattern:
- "(<value>)" where value can be:
- A numeric budget (e.g., "(8192)", "(16384)")
- A reasoning effort level (e.g., "(high)", "(medium)", "(low)")
Examples:
- "claude-sonnet-4-5-20250929(16384)" → budget=16384
- "gpt-5.1(high)" → reasoning_effort="high"
- "gemini-2.5-pro(32768)" → budget=32768
Note: Empty parentheses "()" are not supported and will be ignored.
func ParseProviderPrefix ¶
ParseProviderPrefix extracts provider prefix from model name. Syntax: "provider:model" where ":" is the separator. Returns (provider, model) or ("", originalModel) if no prefix.
Examples:
- "ollama:llama3.2" -> ("ollama", "llama3.2")
- "geminicli:" -> ("geminicli", "")
- "llama3.2" -> ("", "llama3.2")
- "meta-llama/llama-4" -> ("", "meta-llama/llama-4")
func PrintSSHTunnelInstructions ¶
func PrintSSHTunnelInstructions(port int)
PrintSSHTunnelInstructions detects the IP address and prints SSH tunnel instructions for the user to connect to the local OAuth callback server from a remote machine.
Parameters:
- port: The local port number for the SSH tunnel
func ReasoningEffortFromMetadata ¶
ReasoningEffortFromMetadata resolves a reasoning effort string from metadata, inferring "auto" and "none" when budgets request dynamic or disabled thinking.
func RenameKey ¶
RenameKey renames a key in a JSON string by moving its value to a new key path and then deleting the old key path.
Parameters:
- jsonStr: The JSON string to modify
- oldKeyPath: The dot-notation path to the key that should be renamed
- newKeyPath: The dot-notation path where the value should be moved to
Returns:
- string: The modified JSON string with the key renamed
- error: An error if the operation fails
The function performs the rename in two steps: 1. Sets the value at the new key path 2. Deletes the old key path
func ResolveAuthDir ¶
ResolveAuthDir normalizes the auth directory path for consistent reuse throughout the app. It expands a leading tilde (~) to the user's home directory and returns a cleaned path.
func ResolveAutoModel ¶
ResolveAutoModel resolves the "auto" model name to an actual available model. It uses an empty handler type to get any available model from the registry.
Parameters:
- modelName: The model name to check (should be "auto")
- priorityList: Optional list of model IDs to prioritize
Returns:
- string: The resolved model name, or the original if not "auto" or resolution fails
func ResolveClaudeThinkingConfig ¶
ResolveClaudeThinkingConfig resolves thinking configuration from metadata for Claude models. It uses the unified ResolveThinkingConfigFromMetadata and normalizes the budget. Returns the normalized budget (nil if thinking should not be enabled) and whether it matched.
func ResolveOriginalModel ¶
ResolveOriginalModel returns the original model name stored in metadata (if present), otherwise falls back to the provided model.
func ResolveThinkingConfigFromMetadata ¶
ResolveThinkingConfigFromMetadata derives thinking budget/include overrides, converting reasoning effort strings into budgets when possible.
func SanitizeThinkingPart ¶
SanitizeThinkingPart normalizes a thinking part to a canonical form. Strips cache_control and other non-essential fields. Returns the sanitized part as JSON string.
func SecureWrite ¶
func SecureWrite(sb *StateBox, path string, data []byte, opts *SecureWriteOptions) error
SecureWrite atomically writes data to a file using the rename-swap pattern. It writes to a temporary file first, calls fsync(), then atomically renames to the target path. This ensures that power failures or crashes do not corrupt the target file.
If sb is in read-only mode, returns ErrReadOnlyMode without modifying any files. If opts is nil, default options are used (no backup, 0600 permissions).
The atomic rename is guaranteed on Unix systems. On Windows, os.Rename() is atomic on NTFS when source and destination are on the same volume.
func SecureWriteJSON ¶
func SecureWriteJSON(sb *StateBox, path string, v interface{}, opts *SecureWriteOptions) error
SecureWriteJSON marshals data to JSON with indentation and writes it atomically. It uses SecureWrite internally, providing the same atomicity guarantees.
If sb is in read-only mode, returns ErrReadOnlyMode without modifying any files. If opts is nil, default options are used (no backup, 0600 permissions).
func SetLogLevel ¶
SetLogLevel configures the logrus log level based on the configuration. It sets the log level to DebugLevel if debug mode is enabled, otherwise to InfoLevel.
func SetProxy ¶
SetProxy configures the provided HTTP client with proxy settings from the configuration. It supports SOCKS5, HTTP, and HTTPS proxies. The function modifies the client's transport to route requests through the configured proxy server.
func StripCacheControl ¶
StripCacheControl removes cache_control and providerOptions from a JSON object.
func StripThinkingConfigIfUnsupported ¶
StripThinkingConfigIfUnsupported removes thinkingConfig from the request body when the target model does not advertise Thinking capability. It cleans both standard Gemini and Gemini CLI JSON envelopes. This acts as a final safety net in case upstream injected thinking for an unsupported model.
func ThinkingBudgetToEffort ¶
ThinkingBudgetToEffort maps a numeric thinking budget (tokens) to a reasoning effort level for level-based models.
Mappings:
- 0 -> "none" (or lowest supported level if model doesn't support "none")
- -1 -> "auto"
- 1..1024 -> "low"
- 1025..8192 -> "medium"
- 8193..24576 -> "high"
- 24577.. -> highest supported level for the model (defaults to "xhigh")
Returns false when the budget is unsupported (negative values other than -1).
func ThinkingBudgetToGemini3Level ¶
ThinkingBudgetToGemini3Level converts a thinkingBudget to a thinkingLevel for Gemini 3 models. This provides backward compatibility when thinkingBudget is provided for Gemini 3 models. Returns the appropriate thinkingLevel and true if conversion is possible.
func ThinkingEffortToBudget ¶
ThinkingEffortToBudget maps a reasoning effort level to a numeric thinking budget (tokens), clamping the result to the model's supported range.
Mappings (values are normalized to model's supported range):
- "none" -> 0
- "auto" -> -1
- "minimal" -> 512
- "low" -> 1024
- "medium" -> 8192
- "high" -> 24576
- "xhigh" -> 32768
Returns false when the effort level is empty or unsupported.
func ThinkingFromMetadata ¶
ThinkingFromMetadata extracts thinking overrides from metadata produced by NormalizeThinkingModel. It accepts both the new generic keys and legacy Gemini-specific keys.
func ThinkingLevelToBudget ¶
ThinkingLevelToBudget maps a Gemini thinkingLevel to a numeric thinking budget (tokens).
Mappings:
- "minimal" -> 512
- "low" -> 1024
- "medium" -> 8192
- "high" -> 32768
Returns false when the level is empty or unsupported.
func ValidateGemini3ThinkingLevel ¶
ValidateGemini3ThinkingLevel validates that the thinkingLevel is valid for the Gemini 3 model variant. Returns the validated level (normalized to lowercase) and true if valid, or empty string and false if invalid.
func ValidateProviderPrefix ¶
ValidateProviderPrefix checks if a provider prefix is valid and active. Returns the actual model name if valid, or an error with helpful message.
func Walk ¶
Walk recursively traverses a JSON structure to find all occurrences of a specific field. It builds paths to each occurrence and adds them to the provided paths slice.
Parameters:
- value: The gjson.Result object to traverse
- path: The current path in the JSON structure (empty string for root)
- field: The field name to search for
- paths: Pointer to a slice where found paths will be stored
The function works recursively, building dot-notation paths to each occurrence of the specified field throughout the JSON structure.
func WritablePath ¶
func WritablePath() string
WritablePath returns the cleaned WRITABLE_PATH environment variable when it is set. It accepts both uppercase and lowercase variants for compatibility with existing conventions.
Types ¶
type AuditResult ¶
type AuditResult struct {
Path string // The file or directory path
CurrentMode os.FileMode // The current permission mode
RequiredMode os.FileMode // The required permission mode
WasCorrected bool // Whether permissions were corrected
Error error // Any error encountered during audit or correction
}
AuditResult contains the results of a permission audit for a single file or directory.
func AuditPermissions ¶
func AuditPermissions(sb *StateBox) ([]AuditResult, error)
AuditPermissions checks permissions in the State Box without modifying them. It returns a slice of AuditResult entries for each file and directory examined. Directories should have 0700 permissions, and sensitive files (.db, .json) should have 0600.
type ProviderError ¶
type ProviderError struct {
Type ProviderErrorType
Provider string
Model string
Message string
AvailableProviders []string
}
ProviderError represents an error related to provider routing.
func (*ProviderError) Error ¶
func (e *ProviderError) Error() string
type ProviderErrorType ¶
type ProviderErrorType int
ProviderErrorType defines the type of provider-related error.
const ( // ErrUnknownProvider indicates the provider prefix is not recognized. ErrUnknownProvider ProviderErrorType = iota // ErrProviderNotConfigured indicates the provider exists but has no active credentials. ErrProviderNotConfigured // ErrModelNotAvailable indicates the model is not available from the specified provider. ErrModelNotAvailable )
type SecureWriteOptions ¶
type SecureWriteOptions struct {
// CreateBackup creates a .bak file before overwriting an existing file
CreateBackup bool
// Permissions sets the file permissions (default: 0600)
Permissions os.FileMode
}
SecureWriteOptions configures the secure write operation.
func DefaultSecureWriteOptions ¶
func DefaultSecureWriteOptions() *SecureWriteOptions
DefaultSecureWriteOptions returns the default options for SecureWrite.
type StateBox ¶
type StateBox struct {
// contains filtered or unexported fields
}
StateBox manages the canonical state directory for switchAILocal. It provides centralized path resolution for all mutable application data, ensuring consistent handling of environment variables and preventing ghost directories.
func NewStateBox ¶
NewStateBox creates a new StateBox instance. It reads SWITCHAI_STATE_DIR and SWITCHAI_READONLY from environment variables. If SWITCHAI_STATE_DIR is not set, it defaults to ~/.switchailocal. If SWITCHAI_READONLY is set to "1", the StateBox operates in read-only mode.
func NewStateBoxWithAuthDir ¶
NewStateBoxWithAuthDir creates a new StateBox instance with optional legacy auth-dir support. If legacyAuthDir is provided and non-empty, it will be used for credential operations to maintain backward compatibility with existing auth-dir configurations.
func (*StateBox) CredentialPath ¶
CredentialPath returns the full path for a credential file for the given provider. The provider name is sanitized to prevent path traversal attacks.
func (*StateBox) CredentialsDir ¶
CredentialsDir returns the path to the credentials subdirectory. If a legacy auth-dir was configured, it returns that path for backward compatibility. Otherwise, it returns the State Box credentials directory.
func (*StateBox) DiscoveryDir ¶
DiscoveryDir returns the path to the discovery subdirectory.
func (*StateBox) EnsureCredentialsDir ¶
EnsureCredentialsDir creates the credentials directory with 0700 permissions if it doesn't exist. Returns the path to the credentials directory.
func (*StateBox) EnsureDir ¶
EnsureDir creates a directory with secure permissions (0700) if it doesn't exist. It creates all necessary parent directories as well. Returns an error if the directory cannot be created.
func (*StateBox) HasLegacyAuthDir ¶
HasLegacyAuthDir returns true if a legacy auth-dir was configured.
func (*StateBox) IntelligenceDir ¶
IntelligenceDir returns the path to the intelligence subdirectory.
func (*StateBox) IsReadOnly ¶
IsReadOnly returns whether the State Box is in read-only mode.
func (*StateBox) ReadCredential ¶
ReadCredential reads a credential file for the given provider and unmarshals it into v. Returns os.ErrNotExist if the credential file does not exist.
func (*StateBox) ResolvePath ¶
ResolvePath joins a relative path with the State Box root. If the path is already absolute or starts with tilde, it is returned as-is after cleaning. Otherwise, it is joined with the State Box root directory.
func (*StateBox) SetLegacyAuthDir ¶
SetLegacyAuthDir sets the legacy auth-dir for backward compatibility. This should be called during initialization if the config has an auth-dir set.
func (*StateBox) WriteCredential ¶
WriteCredential writes a credential to a file for the given provider using atomic writes. Returns ErrReadOnlyMode if the StateBox is in read-only mode.