Per-session workspace, tools only when workspace set
- Session has workspace field (absolute path, optional) - ToolService stateless, receives workspace per call - No workspace = no tools sent to API - Removed hardcoded workspace from Program.cs - Workspace field in session edit form
This commit is contained in:
1 parent
002ef293f0
commit
b752c99a64
9 files changed
+50
-40
No files matched your search
@@ -32,6 +32,7 @@ public class AnthropicService
|
||||
List<object> systemPrompt,
|
||||
List<object> messages,
|
||||
string chainId,
|
||||
string workspace,
|
||||
string sessionId = null,
|
||||
int maxIterations = 25)
|
||||
{
|
||||
@@ -57,10 +58,12 @@ public class AnthropicService
|
||||
["max_tokens"] = 4096,
|
||||
["system"] = systemPrompt,
|
||||
["messages"] = messages,
|
||||
["tools"] = _toolService.GetToolDefinitions(),
|
||||
["stream"] = true
|
||||
};
|
||||
|
||||
if (!string.IsNullOrEmpty(workspace))
|
||||
payload["tools"] = _toolService.GetToolDefinitions();
|
||||
|
||||
var client = CreateClient();
|
||||
var json = JsonSerializer.Serialize(payload);
|
||||
var httpContent = new StringContent(json, Encoding.UTF8, "application/json");
|
||||
@@ -86,7 +89,7 @@ public class AnthropicService
|
||||
return;
|
||||
}
|
||||
|
||||
var (stopReason, assistantContent, executedTools) = await ProcessStream(response, httpResp, usage);
|
||||
var (stopReason, assistantContent, executedTools) = await ProcessStream(response, httpResp, usage, workspace);
|
||||
lastStopReason = stopReason;
|
||||
|
||||
messages.Add(new { role = "assistant", content = assistantContent });
|
||||
@@ -181,7 +184,8 @@ public class AnthropicService
|
||||
private async Task<(string StopReason, List<object> Content, List<ToolResult> Tools)> ProcessStream(
|
||||
HttpResponse response,
|
||||
HttpResponseMessage httpResp,
|
||||
TokenUsage usage)
|
||||
TokenUsage usage,
|
||||
string workspace)
|
||||
{
|
||||
var stream = await httpResp.Content.ReadAsStreamAsync();
|
||||
using var reader = new StreamReader(stream);
|
||||
@@ -297,7 +301,7 @@ public class AnthropicService
|
||||
|
||||
string toolResult;
|
||||
bool isError = false;
|
||||
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl); }
|
||||
try { toolResult = await _toolService.ExecuteTool(currentToolName, inputEl, workspace); }
|
||||
catch (Exception ex) { toolResult = $"Fehler: {ex.Message}"; isError = true; }
|
||||
|
||||
executedTools.Add(new ToolResult(currentToolId, toolResult, isError));
|
||||
|
||||
@@ -101,6 +101,7 @@ public class DatabaseService
|
||||
TryAddColumn(conn, "sessions", "agent_id", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "description", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "image", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "workspace", "TEXT");
|
||||
TryAddColumn(conn, "sessions", "is_deleted", "INTEGER NOT NULL DEFAULT 0");
|
||||
TryAddColumn(conn, "chains", "agent_id", "TEXT");
|
||||
}
|
||||
@@ -162,7 +163,7 @@ public class DatabaseService
|
||||
|
||||
// ── Sessions ──
|
||||
|
||||
public async Task<SessionRecord> CreateSession(string name = null, string description = null, string image = null, string agentId = null)
|
||||
public async Task<SessionRecord> CreateSession(string name = null, string description = null, string image = null, string agentId = null, string workspace = null)
|
||||
{
|
||||
using var conn = CreateConnection();
|
||||
var now = DateTime.UtcNow.ToString("o");
|
||||
@@ -173,12 +174,13 @@ public class DatabaseService
|
||||
Description = description,
|
||||
Image = image,
|
||||
AgentId = agentId,
|
||||
Workspace = workspace,
|
||||
CreatedAt = now,
|
||||
UpdatedAt = now
|
||||
};
|
||||
await conn.ExecuteAsync(@"
|
||||
INSERT INTO sessions (session_id, name, description, image, agent_id, created_at, updated_at)
|
||||
VALUES (@SessionId, @Name, @Description, @Image, @AgentId, @CreatedAt, @UpdatedAt)
|
||||
INSERT INTO sessions (session_id, name, description, image, agent_id, workspace, created_at, updated_at)
|
||||
VALUES (@SessionId, @Name, @Description, @Image, @AgentId, @Workspace, @CreatedAt, @UpdatedAt)
|
||||
", session);
|
||||
return session;
|
||||
}
|
||||
@@ -217,6 +219,7 @@ public class DatabaseService
|
||||
if (body.TryGetProperty("description", out var d)) { sets.Add("description = @desc"); p.Add("desc", d.GetString()); }
|
||||
if (body.TryGetProperty("image", out var img)) { sets.Add("image = @image"); p.Add("image", img.GetString()); }
|
||||
if (body.TryGetProperty("agentId", out var a)) { sets.Add("agent_id = @agentId2"); p.Add("agentId2", a.GetString()); }
|
||||
if (body.TryGetProperty("workspace", out var w)) { sets.Add("workspace = @workspace"); p.Add("workspace", w.GetString()); }
|
||||
|
||||
await conn.ExecuteAsync($"UPDATE sessions SET {string.Join(", ", sets)} WHERE session_id = @sessionId", p);
|
||||
}
|
||||
|
||||
+20
-27
@@ -11,13 +11,6 @@ namespace Gott.Services;
|
||||
|
||||
public class ToolService
|
||||
{
|
||||
private readonly string _workspaceRoot;
|
||||
|
||||
public ToolService(string workspaceRoot)
|
||||
{
|
||||
_workspaceRoot = workspaceRoot;
|
||||
}
|
||||
|
||||
public object[] GetToolDefinitions()
|
||||
{
|
||||
return new object[]
|
||||
@@ -112,31 +105,31 @@ public class ToolService
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<string> ExecuteTool(string name, JsonElement input)
|
||||
public async Task<string> ExecuteTool(string name, JsonElement input, string workspace)
|
||||
{
|
||||
switch (name)
|
||||
{
|
||||
case "read_file":
|
||||
{
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "");
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "", workspace);
|
||||
if (!File.Exists(path))
|
||||
return $"Fehler: Datei nicht gefunden: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Fehler: Datei nicht gefunden: {Path.GetRelativePath(workspace, path)}";
|
||||
return File.ReadAllText(path);
|
||||
}
|
||||
case "write_file":
|
||||
{
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "");
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "", workspace);
|
||||
var dir = Path.GetDirectoryName(path);
|
||||
if (dir != null) Directory.CreateDirectory(dir);
|
||||
var content = input.GetProperty("content").GetString() ?? "";
|
||||
File.WriteAllText(path, content);
|
||||
return $"Datei geschrieben: {Path.GetRelativePath(_workspaceRoot, path)} ({content.Length} Zeichen)";
|
||||
return $"Datei geschrieben: {Path.GetRelativePath(workspace, path)} ({content.Length} Zeichen)";
|
||||
}
|
||||
case "edit_file":
|
||||
{
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "");
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "", workspace);
|
||||
if (!File.Exists(path))
|
||||
return $"Fehler: Datei nicht gefunden: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Fehler: Datei nicht gefunden: {Path.GetRelativePath(workspace, path)}";
|
||||
var fileContent = File.ReadAllText(path);
|
||||
var oldText = input.GetProperty("old_text").GetString() ?? "";
|
||||
var newText = input.GetProperty("new_text").GetString() ?? "";
|
||||
@@ -144,34 +137,34 @@ public class ToolService
|
||||
return "Fehler: old_text nicht in der Datei gefunden. Exakter Match nötig.";
|
||||
fileContent = fileContent.Replace(oldText, newText);
|
||||
File.WriteAllText(path, fileContent);
|
||||
return $"Datei bearbeitet: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Datei bearbeitet: {Path.GetRelativePath(workspace, path)}";
|
||||
}
|
||||
case "list_files":
|
||||
{
|
||||
var path = ResolvePath(input.TryGetProperty("path", out var p) ? p.GetString() ?? "" : "");
|
||||
var path = ResolvePath(input.TryGetProperty("path", out var p) ? p.GetString() ?? "" : "", workspace);
|
||||
if (!Directory.Exists(path))
|
||||
return $"Fehler: Verzeichnis nicht gefunden: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Fehler: Verzeichnis nicht gefunden: {Path.GetRelativePath(workspace, path)}";
|
||||
var entries = new List<string>();
|
||||
foreach (var dir in Directory.GetDirectories(path))
|
||||
entries.Add(Path.GetRelativePath(_workspaceRoot, dir) + "/");
|
||||
entries.Add(Path.GetRelativePath(workspace, dir) + "/");
|
||||
foreach (var file in Directory.GetFiles(path))
|
||||
entries.Add(Path.GetRelativePath(_workspaceRoot, file));
|
||||
entries.Add(Path.GetRelativePath(workspace, file));
|
||||
return entries.Count == 0 ? "(leer)" : string.Join("\n", entries);
|
||||
}
|
||||
case "delete_file":
|
||||
{
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "");
|
||||
var path = ResolvePath(input.GetProperty("path").GetString() ?? "", workspace);
|
||||
if (File.Exists(path))
|
||||
{
|
||||
File.Delete(path);
|
||||
return $"Datei gelöscht: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Datei gelöscht: {Path.GetRelativePath(workspace, path)}";
|
||||
}
|
||||
if (Directory.Exists(path))
|
||||
{
|
||||
Directory.Delete(path, recursive: false);
|
||||
return $"Verzeichnis gelöscht: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Verzeichnis gelöscht: {Path.GetRelativePath(workspace, path)}";
|
||||
}
|
||||
return $"Fehler: Nicht gefunden: {Path.GetRelativePath(_workspaceRoot, path)}";
|
||||
return $"Fehler: Nicht gefunden: {Path.GetRelativePath(workspace, path)}";
|
||||
}
|
||||
case "execute":
|
||||
{
|
||||
@@ -179,7 +172,7 @@ public class ToolService
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = "/bin/bash",
|
||||
WorkingDirectory = _workspaceRoot,
|
||||
WorkingDirectory = workspace,
|
||||
RedirectStandardInput = true,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
@@ -218,10 +211,10 @@ public class ToolService
|
||||
}
|
||||
}
|
||||
|
||||
private string ResolvePath(string relativePath)
|
||||
private string ResolvePath(string relativePath, string workspace)
|
||||
{
|
||||
var full = Path.GetFullPath(Path.Combine(_workspaceRoot, relativePath ?? ""));
|
||||
if (!full.StartsWith(_workspaceRoot))
|
||||
var full = Path.GetFullPath(Path.Combine(workspace, relativePath ?? ""));
|
||||
if (!full.StartsWith(workspace))
|
||||
throw new Exception("Zugriff außerhalb des Workspace nicht erlaubt");
|
||||
return full;
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user