feat(ai): improve runtime capability reporting and context settings

Add explicit chat capability tracking, expose formatted runtime capabilities
in the info command, and support a max context size option for user AI settings.

Also update Ollama base URL resolution to use OLLAMA_ADDRESS and simplify
provider chat command execution.
This commit is contained in:
2026-05-11 02:01:44 +03:00
parent d2464b9b21
commit 3848dd82d9
15 changed files with 850 additions and 336 deletions
+38 -44
View File
@@ -75,8 +75,9 @@ const TELEGRAM_LIMIT = 4096;
const MAX_TOOL_ROUNDS = 20;
const OPENAI_IMAGE_PARTIALS = 3;
const AI_REQUEST_TIMEOUT_MS = 10 * 60 * 1000;
const DEFAULT_OLLAMA_CONTEXT_SIZE = 256000;
const MIN_OLLAMA_CONTEXT_SIZE = 4096;
const MAX_OLLAMA_CONTEXT_SIZE = 262144;
const DEFAULT_OLLAMA_CONTEXT_SIZE = 32768;
const toolResourceLocks = new KeyedAsyncLock();
type UnifiedRunOptions = {
@@ -134,11 +135,7 @@ type RuntimeConfigSnapshot = {
useNamesInPrompt: boolean;
useSystemPrompt: boolean;
systemPrompt?: string;
ollamaModel: string;
ollamaImageModel: string;
ollamaThinkModel: string;
ollamaAudioModel: string;
ollamaEmbeddingModel: string;
ollamaChatTarget: AiRuntimeTarget;
ollamaVisionTarget: AiRuntimeTarget;
ollamaThinkingTarget: AiRuntimeTarget;
@@ -152,30 +149,23 @@ type RuntimeConfigSnapshot = {
ollamaRagMaxArchiveFiles: number;
ollamaRagMaxArchiveBytes: number;
ollamaRagMaxArchiveDepth: number;
geminiModel: string;
geminiImageModel: string;
geminiTranscriptionModel: string;
geminiChatTarget: AiRuntimeTarget;
mistralModel: string;
mistralTranscriptionModel: string;
mistralChatTarget: AiRuntimeTarget;
openAiModel: string;
openAiImageModel: string;
openAiTranscriptionModel: string;
openAiChatTarget: AiRuntimeTarget;
openAiImageTarget: AiRuntimeTarget;
};
function snapshotRuntimeConfig(): RuntimeConfigSnapshot {
// TODO: 11/05/2026, Danil Nikolaev: remove export
export function snapshotRuntimeConfig(): RuntimeConfigSnapshot {
return {
useNamesInPrompt: Environment.USE_NAMES_IN_PROMPT,
useSystemPrompt: Environment.USE_SYSTEM_PROMPT,
systemPrompt: Environment.SYSTEM_PROMPT,
ollamaModel: Environment.OLLAMA_CHAT_MODEL,
ollamaImageModel: Environment.OLLAMA_IMAGE_MODEL,
ollamaThinkModel: Environment.OLLAMA_THINK_MODEL,
ollamaAudioModel: Environment.OLLAMA_AUDIO_MODEL,
ollamaEmbeddingModel: Environment.OLLAMA_EMBEDDING_MODEL,
ollamaChatTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "chat"),
ollamaVisionTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "vision"),
ollamaThinkingTarget: resolveAiRuntimeTarget(AiProvider.OLLAMA, "thinking"),
@@ -189,16 +179,11 @@ function snapshotRuntimeConfig(): RuntimeConfigSnapshot {
ollamaRagMaxArchiveFiles: Environment.OLLAMA_RAG_MAX_ARCHIVE_FILES,
ollamaRagMaxArchiveBytes: Environment.OLLAMA_RAG_MAX_ARCHIVE_BYTES,
ollamaRagMaxArchiveDepth: Environment.OLLAMA_RAG_MAX_ARCHIVE_DEPTH,
geminiModel: Environment.GEMINI_MODEL,
geminiImageModel: Environment.GEMINI_IMAGE_MODEL,
geminiTranscriptionModel: Environment.GEMINI_TRANSCRIPTION_MODEL,
geminiChatTarget: resolveAiRuntimeTarget(AiProvider.GEMINI, "chat"),
mistralModel: Environment.MISTRAL_MODEL,
mistralTranscriptionModel: Environment.MISTRAL_TRANSCRIPTION_MODEL,
mistralChatTarget: resolveAiRuntimeTarget(AiProvider.MISTRAL, "chat"),
openAiModel: Environment.OPENAI_MODEL,
openAiImageModel: Environment.OPENAI_IMAGE_MODEL,
openAiTranscriptionModel: Environment.OPENAI_TRANSCRIPTION_MODEL,
openAiChatTarget: resolveAiRuntimeTarget(AiProvider.OPENAI, "chat"),
openAiImageTarget: resolveAiRuntimeTarget(AiProvider.OPENAI, "outputImages"),
};
@@ -234,12 +219,12 @@ function geminiAudioMimeType(mimeType: string | undefined): string {
}
}
function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): string {
export function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): string {
switch (provider) {
case AiProvider.OLLAMA:
return config.ollamaChatTarget.model;
case AiProvider.GEMINI:
return config.geminiModel;
return config.geminiChatTarget.model;
case AiProvider.MISTRAL:
return config.mistralChatTarget.model;
case AiProvider.OPENAI:
@@ -247,6 +232,25 @@ function snapshotModel(provider: AiProvider, config: RuntimeConfigSnapshot): str
}
}
export function providerTargets(provider: AiProvider, config: RuntimeConfigSnapshot): AiRuntimeTarget[] {
switch (provider) {
case AiProvider.OLLAMA:
return [
config.ollamaChatTarget,
config.ollamaVisionTarget,
config.ollamaThinkingTarget,
config.ollamaAudioTarget,
config.ollamaDocumentsTarget
];
case AiProvider.GEMINI:
return [config.geminiChatTarget];
case AiProvider.MISTRAL:
return [config.mistralChatTarget];
case AiProvider.OPENAI:
return [config.openAiChatTarget];
}
}
function providerName(provider: AiProvider): AiProviderName {
switch (provider) {
case AiProvider.OLLAMA:
@@ -1422,12 +1426,10 @@ async function runOllama(
const modelInfo = await ollama.show({model: model});
const contextKey = Object.keys(modelInfo.model_info).find(k => k.endsWith(".context_length"));
// @ts-ignore
const maxContextLength = contextKey ? <number>modelInfo?.model_info?.[contextKey] : 32768;
const context = clamp(contextSize ?? DEFAULT_OLLAMA_CONTEXT_SIZE, MIN_OLLAMA_CONTEXT_SIZE, maxContextLength ?? 32768);
const maxContextLength = contextKey ? <number>modelInfo?.model_info?.[contextKey] : DEFAULT_OLLAMA_CONTEXT_SIZE;
const context = clamp(contextSize ?? contextSize === -1 ? MAX_OLLAMA_CONTEXT_SIZE : DEFAULT_OLLAMA_CONTEXT_SIZE, MIN_OLLAMA_CONTEXT_SIZE, maxContextLength ?? DEFAULT_OLLAMA_CONTEXT_SIZE);
console.log("MESSAGES", messages);
await unloadAllOllamaModels(ollama, [model, config.ollamaEmbeddingModel]);
await unloadAllOllamaModels(ollama, [model, config.ollamaDocumentsTarget.model]);
if (!(await isOllamaModelActive(target))) {
const currentStatus = streamMessage.getStatus();
@@ -1466,15 +1468,7 @@ async function runOllama(
try {
for (let round = 0; round < MAX_TOOL_ROUNDS; round++) {
// if (round === 0) {
// streamMessage.setStatus(Environment.weightsLoadingText);
// await streamMessage.flush();
// } else {
// streamMessage.setStatus(roundStatus(round, firstRoundStatus));
// await streamMessage.flush();
// }
console.log("MAX_CONTENT_LENGTH", context);
console.log("CONTENT_LENGTH", context);
const request: ChatRequest = {
model: model,