feat(ai): chat continuation for photo insights (server v1)
Adds POST /insights/chat and GET /insights/chat/history. Replays the stored agentic conversation through the same backend the insight was generated with (or a per-turn override), runs a short tool-calling loop, and persists the extended history in append or amend mode. Backend switching: same-backend or hybrid->local replay verbatim; local->hybrid is rejected in v1 (would require on-the-fly vision description rewrite). Per-(library, file) async mutex serialises concurrent turns. Soft context budget drops oldest tool_call+result pairs when the serialized history exceeds num_ctx - 2048 tokens. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -96,7 +96,7 @@ impl InsightGenerator {
|
||||
/// first root under which the file exists. Insights may be generated
|
||||
/// for any library — the generator itself doesn't know which — so we
|
||||
/// probe each root rather than trust a single `base_path`.
|
||||
fn resolve_full_path(&self, rel_path: &str) -> Option<std::path::PathBuf> {
|
||||
pub(crate) fn resolve_full_path(&self, rel_path: &str) -> Option<std::path::PathBuf> {
|
||||
use std::path::Path;
|
||||
for lib in &self.libraries {
|
||||
let candidate = Path::new(&lib.root_path).join(rel_path);
|
||||
@@ -129,7 +129,7 @@ impl InsightGenerator {
|
||||
|
||||
/// Load image file, resize it, and encode as base64 for vision models
|
||||
/// Resizes to max 1024px on longest edge to reduce context usage
|
||||
fn load_image_as_base64(&self, file_path: &str) -> Result<String> {
|
||||
pub(crate) fn load_image_as_base64(&self, file_path: &str) -> Result<String> {
|
||||
use image::imageops::FilterType;
|
||||
|
||||
let full_path = self.resolve_full_path(file_path).ok_or_else(|| {
|
||||
@@ -1411,7 +1411,7 @@ Return ONLY the summary, nothing else."#,
|
||||
// ── Tool executors for agentic loop ────────────────────────────────
|
||||
|
||||
/// Dispatch a tool call to the appropriate executor
|
||||
async fn execute_tool(
|
||||
pub(crate) async fn execute_tool(
|
||||
&self,
|
||||
tool_name: &str,
|
||||
arguments: &serde_json::Value,
|
||||
@@ -2136,7 +2136,7 @@ Return ONLY the summary, nothing else."#,
|
||||
// ── Agentic insight generation ──────────────────────────────────────
|
||||
|
||||
/// Build the list of tool definitions for the agentic loop
|
||||
fn build_tool_definitions(has_vision: bool) -> Vec<Tool> {
|
||||
pub(crate) fn build_tool_definitions(has_vision: bool) -> Vec<Tool> {
|
||||
let mut tools = vec![
|
||||
Tool::function(
|
||||
"search_rag",
|
||||
|
||||
Reference in New Issue
Block a user