Ship Assistent 0.14.0: chat sessions, ask-only hops, and context compression.

Per-chat Generate session with sparse deltas; drop Cards/Civitai/wanted hops; rolling history summary via the same Ollama model with a budget chip and /compress.

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Leonid Pershin
2026-08-23 06:19:04 +03:00
co-authored by Cursor
parent d29436c944
commit 9ac24a828b
35 changed files with 5640 additions and 5652 deletions
+33 -6
View File
@@ -297,7 +297,22 @@ public partial class SwarmAssistentExtension
return null;
}
/// <summary>Proxy to Ollama /api/chat (non-stream), with optional Civitai search hop.</summary>
/// <summary>Extract Ollama prompt token count when present.</summary>
static int? ReadPromptEvalCount(JObject raw)
{
if (raw is null)
{
return null;
}
int? n = raw["prompt_eval_count"]?.Value<int?>();
if (n is null || n <= 0)
{
n = raw["promptEvalCount"]?.Value<int?>();
}
return n is > 0 ? n : null;
}
/// <summary>Proxy to Ollama /api/chat (non-stream), with optional ask hops.</summary>
public async Task<JObject> AssistentChat(Session session, string baseUrl, string model, string pack, bool includeBase, JObject raw)
{
ExtractChatPayload(raw, ref baseUrl, ref model, ref pack, ref includeBase, out JArray userMessages, out string contextJson, out string persona, out JArray skills);
@@ -314,7 +329,7 @@ public partial class SwarmAssistentExtension
{
(string reply, JObject parsed, JArray civitai, int systemChars, JObject systemLayers) = await RunChatWithHops(
session, root, modelName, packName, includeBase, contextJson, userMessages, personaId: persona, skillIds: skills, embedModel: embedModel);
return new JObject
JObject result = new()
{
["success"] = true,
["reply"] = reply,
@@ -326,6 +341,12 @@ public partial class SwarmAssistentExtension
["system_chars"] = systemChars,
["system_layers"] = systemLayers,
};
int? promptEval = ReadPromptEvalCount(parsed);
if (promptEval is not null)
{
result["prompt_eval_count"] = promptEval.Value;
}
return result;
}
catch (Exception ex)
{
@@ -333,7 +354,7 @@ public partial class SwarmAssistentExtension
}
}
/// <summary>WebSocket streaming chat (Ollama stream:true) + Civitai hops.</summary>
/// <summary>WebSocket streaming chat (Ollama stream:true) + ask hops.</summary>
public async Task<JObject> AssistentChatWS(Session session, WebSocket ws, string baseUrl, string model, string pack, bool includeBase, JObject raw)
{
ExtractChatPayload(raw, ref baseUrl, ref model, ref pack, ref includeBase, out JArray userMessages, out string contextJson, out string persona, out JArray skills);
@@ -372,13 +393,13 @@ public partial class SwarmAssistentExtension
{
["clear_stream"] = true,
["hop"] = hop + 1,
["notice"] = "Civitai search done — refining…",
["notice"] = "Ask hop — refining…",
}, API.WebsocketTimeout);
}
}
(string reply, JObject parsed, JArray civitai, int systemChars, JObject systemLayers) = await RunChatWithHops(
session, root, modelName, packName, includeBase, contextJson, userMessages, OnDelta, OnHopStart, persona, skills, embedModel);
await ws.SendJson(new JObject
JObject done = new()
{
["success"] = true,
["done"] = true,
@@ -390,7 +411,13 @@ public partial class SwarmAssistentExtension
["civitai_results"] = civitai,
["system_chars"] = systemChars,
["system_layers"] = systemLayers,
}, API.WebsocketTimeout);
};
int? promptEval = ReadPromptEvalCount(parsed);
if (promptEval is not null)
{
done["prompt_eval_count"] = promptEval.Value;
}
await ws.SendJson(done, API.WebsocketTimeout);
}
catch (Exception ex)
{