import { PluginConfig } from './types.js';
import { ModelRegistry } from './model-registry.js';

/**
 * Local LLM client for OpenAI-compatible endpoints (LM Studio, Ollama, MLX, etc.)
 *
 * Based on openclaw-tactician's provider detection patterns for consistency.
 * Provides privacy-preserving, cost-effective LLM operations with
 * graceful fallback to cloud providers when local LLM is unavailable.
 */
type LocalLlmType = "lmstudio" | "ollama" | "mlx" | "vllm" | "generic";
interface LocalModelInfo {
    id: string;
    contextWindow?: number;
    maxTokens?: number;
}
type LocalLlmRequestPriority = "recall-critical" | "background";
interface LocalLlmChatCompletionOptions {
    temperature?: number;
    maxTokens?: number;
    responseFormat?: {
        type: string;
    };
    timeoutMs?: number;
    operation?: string;
    priority?: LocalLlmRequestPriority;
}
interface LocalLlmChatCompletionResult {
    content: string;
    usage?: {
        promptTokens: number;
        completionTokens: number;
        totalTokens: number;
    };
}
declare class LocalLlmClient {
    private config;
    private isAvailable;
    private lastHealthCheck;
    private detectedType;
    private cachedModelInfo;
    private cachedLmsContext;
    private lastLmsCheck;
    private consecutive400s;
    private cooldownUntilMs;
    private modelRegistry?;
    private _disableThinking;
    private readonly requestQueues;
    private readonly queueProcessing;
    private queueDrainScheduled;
    private static readonly HEALTH_CHECK_INTERVAL_MS;
    private static readonly LMS_CACHE_INTERVAL_MS;
    constructor(config: PluginConfig, modelRegistry?: ModelRegistry);
    /**
     * Request thinking/reasoning suppression on the next chat completion.
     *
     * When `true`, the client will inject
     * `chat_template_kwargs: { enable_thinking: false }` into the request
     * body — **but only when the detected backend is known to support it**
     * (LM Studio, vLLM; see `THINKING_COMPATIBLE_BACKENDS`).  Strict
     * OpenAI-compat backends reject unknown fields with 400; on those the
     * client fails open (thinking runs normally).  This is the safe
     * default for Remnic extraction / consolidation: measurable latency
     * win on thinking-capable backends, zero risk on others.  Issue #548.
     */
    set disableThinking(value: boolean);
    private resolveHomeDir;
    private buildRequestHeaders;
    private isAbortError;
    /**
     * Set the ModelRegistry for caching detected capabilities
     */
    setModelRegistry(registry: ModelRegistry): void;
    /**
     * Get the detected server type (null if not detected)
     */
    getDetectedType(): LocalLlmType | null;
    private getBackendKey;
    private getGlobalBackendState;
    private getTrippedBackendState;
    private markBackendUnavailable;
    private extractNonRecoverableBackendReason;
    private extractNonRecoverableBackendReasonFromErrorText;
    private normalizeBackendTripReason;
    /**
     * Fetch with timeout for health checks
     */
    private fetchWithTimeout;
    /**
     * Check if local LLM is available
     * Uses 127.0.0.1 instead of localhost to avoid DNS issues (consistent with tactician)
     */
    checkAvailability(): Promise<boolean>;
    /**
     * Try to get context window from LM Studio settings.json as fallback.
     * This reads the defaultContextLength setting which is what LM Studio uses
     * when loading models without explicit context configuration.
     */
    private getContextFromLmStudioSettings;
    /**
     * Try to get context window from LMS CLI (LM Studio specific).
     * Uses --json flag for reliable parsing.
     * Returns null if LMS CLI is not available or model not found.
     */
    private getContextFromLmsCli;
    /**
     * Get full model info from LMS CLI including context length and max context length.
     * Returns null if LMS CLI is unavailable or model not found.
     */
    private getLmsModelInfo;
    /**
     * Get context window for the configured model, using cache if available.
     * This method caches the result to avoid repeated LMS CLI calls.
     * Order: ModelRegistry (persistent) -> memory cache -> LMS CLI -> settings.json
     */
    getCachedContextWindow(modelId: string): number | null;
    /**
     * Clear the LMS context cache. Call this when the model changes.
     */
    clearContextCache(): void;
    private remainingCooldownMs;
    private scheduleQueueDrain;
    private hasQueuedRequests;
    private dequeueQueuedRequest;
    private failOpenQueuedRequestsForCooldown;
    private startAvailableQueuedRequests;
    private runQueuedRequest;
    private runChatCompletionRequest;
    /**
     * Query the local LLM server for loaded model information.
     * Returns null if unavailable or if the model is not found.
     */
    getLoadedModelInfo(): Promise<LocalModelInfo | null>;
    /**
     * Check if the configured model is available and get its actual context window.
     * Warns if there's a mismatch between expected and actual context.
     */
    validateModelConfig(expectedContextWindow?: number): Promise<{
        available: boolean;
        actualContextWindow?: number;
        warnings: string[];
    }>;
    /**
     * Make a chat completion request to local LLM
     */
    chatCompletion(messages: Array<{
        role: string;
        content: string;
    }>, options?: LocalLlmChatCompletionOptions): Promise<LocalLlmChatCompletionResult | null>;
    /**
     * Estimate tokens when local LLM doesn't return usage stats
     * Rough estimate: 1 token ≈ 4 characters
     */
    private estimateTokens;
    /**
     * Try local LLM first, fallback to cloud provider if configured
     */
    withFallback<T>(localOperation: () => Promise<T | null>, fallbackOperation: () => Promise<T>, operationName: string): Promise<T>;
}

export { LocalLlmClient, type LocalLlmRequestPriority, type LocalLlmType, type LocalModelInfo };
