using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.Json;
using System.Threading.Tasks;
using Gott.Models;
namespace Gott.Services;
public class ChatService
{
private readonly DatabaseService _db;
private readonly ToolService _toolService;
private readonly MemoryService _memoryService;
private readonly SkillService _skillService;
private readonly ServerSettingsService _settings;
public ChatService(DatabaseService db, ToolService toolService, MemoryService memoryService, SkillService skillService,
ServerSettingsService settings)
{
_db = db;
_toolService = toolService;
_memoryService = memoryService;
_skillService = skillService;
_settings = settings;
}
private int MaxHistory => _settings.GetInt("MaxHistory", 220);
private int ReminderInterval => _settings.GetInt("ReminderInterval", 30);
public static string ResolveModelId(string model)
{
// OpenAI-kompatible Provider werden unverändert durchgereicht (Format: "provider:model")
if (OpenAiService.IsOpenAiProvider(model)) return model;
// Defensiv-Strip: alte "[1m]"-Suffixe aus DB transparent normalisieren.
// 1M-Context ist seit März 2026 Default bei Opus 4.6/4.7/4.8 + Sonnet 4.6 – kein Suffix mehr nötig.
if (!string.IsNullOrEmpty(model) && model.Contains("[1m]"))
model = model.Replace("[1m]", "");
return model switch
{
"sonnet" => "claude-sonnet-4-6",
"sonnet-4-5" => "claude-sonnet-4-5",
"sonnet-4-6" => "claude-sonnet-4-6",
"opus" => "claude-opus-4-7",
"opus-4-5" => "claude-opus-4-5",
"opus-4-6" => "claude-opus-4-6",
"opus-4-7" => "claude-opus-4-7",
"haiku" => "claude-haiku-4-5",
"haiku-4-5" => "claude-haiku-4-5",
_ => "claude-sonnet-4-6"
};
}
///
/// Baut den vollständigen ChatContext auf: Agent, Model, SystemPrompt, Chain, Workspace etc.
///
public async Task PrepareContext(string sessionId, string userId, int maxIterations = 100, bool planMode = false, bool hasRemoteConnection = false, string remoteOs = null)
{
var session = await _db.GetSession(sessionId);
if (session == null || (userId != null && session.UserId != userId) || string.IsNullOrEmpty(session.AgentId))
return null;
var agent = await _db.GetAgent(session.AgentId);
if (agent == null)
return null;
var modelId = ResolveModelId(agent.Model);
var markedMessages = await _db.GetMarkedMessages(sessionId);
var agentMemoryDir = _memoryService.GetAgentMemoryDir(agent.AgentId);
var agentSkills = await _db.GetAgentSkills(agent.AgentId);
var user = userId != null ? await _db.GetUser(userId) : null;
var workspace = WorkspaceHelper.ResolveWorkspace(session, user);
if (string.IsNullOrEmpty(workspace) && agentSkills.Any())
workspace = WorkspaceHelper.TempDir;
var promptBlocks = SystemPromptBuilder.BuildBlocks(
agent, session, markedMessages, _memoryService, agentMemoryDir,
agentSkills, _skillService, userPersona: user?.GetEffectivePersona(),
resolvedWorkspace: workspace, planMode: planMode);
var systemPromptText = string.Join("\n\n", promptBlocks.Select(b => b.Content));
var hasWorkspace = !string.IsNullOrEmpty(workspace);
var includeMemory = agent.UseMemory && !string.IsNullOrEmpty(agent.AgentId);
var includeGitHubSearch = agent.UseGitHubSearch && _toolService.HasGitHubToken;
var includeSerperSearch = agent.UseSerperSearch && _toolService.HasSerperKey;
var hasTools = hasWorkspace || includeMemory || includeGitHubSearch || includeSerperSearch;
var toolChoice = hasTools ? "any" : null;
var toolDefs = hasTools
? _toolService.GetToolDefinitions(new ToolDefinitionOptions
{
IncludeFileTools = hasWorkspace, IncludeMemory = includeMemory,
IncludeUserChoice = agent.AllowUserChoice, IncludeRemote = hasRemoteConnection, RemoteOs = remoteOs,
IncludeGitHubSearch = includeGitHubSearch, IncludeSerperSearch = includeSerperSearch
})
: Array.Empty