feat: Telegram file/image support, long-term memory, parallel ad-hoc subagents, zero-trust verifier#31
Conversation
|
@copilot use ocrs https://github.com/robertknight/ocrs as ocr image instead of terract |
…rs OCR Co-authored-by: chinkan <[email protected]> Agent-Logs-Url: https://github.com/chinkan/RustFox/sessions/9c502a20-dd1e-47bb-8b4d-34cf2f26c7f1
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Add MessageContent enum supporting Text(String) and Parts(Vec<ContentPart>) to replace Option<String> in ChatMessage.content. Backwards-compatible: Text variant serializes as plain JSON string, Parts as JSON array. - Add ContentPart, ImageUrlContent, MessageContent types to llm.rs - Update ChatMessage.content: Option<String> → Option<MessageContent> - Fix all usage sites in agent.rs, memory/conversations.rs, memory/query_rewriter.rs, memory/summarizer.rs, memory/rag.rs - DB storage continues to use extracted plain text via as_text() - All 113 tests pass Co-authored-by: Copilot <[email protected]>
- Add `supports_vision` bool field to `OpenRouterConfig` (default false) - Add `OcrConfig` struct with `model_dir` (default $HOME/.cache/ocrs) - Add `ocr` field to `Config` with serde default - Add 3 tests: supports_vision defaults false, parses true, ocr default dir - Update config.example.toml with comments for new fields Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
- Add src/file_processor/mod.rs with process_attachments() entry point - Image handling: base64 vision parts (supports_vision=true) or OCR via ocrs - OCR: auto-downloads text-detection/recognition .rten models on first use - PDF: text extraction via pdf_extract::extract_text_from_mem - DOCX: text extraction by traversing docx_rs document children - Long context (>6000 chars): chunk+store in MemoryStore, RAG-retrieve - Unit tests for chunk_text (4 cases) - Register module via mod file_processor; in src/main.rs Co-authored-by: Copilot <[email protected]>
…comment on pdf unwrap_or_default Co-authored-by: Copilot <[email protected]>
- Replace early-return for non-text messages with caption fallback - Add download_telegram_file helper (creates temp dir, fetches via Telegram API, detects MIME with infer crate) - Add classify_attachment_kind and mime_from_extension helpers - Collect photo and document attachments into Vec<Attachment> - Pass attachments to IncomingMessage instead of empty vec - Clean up temp dir after agent processing completes - Add 5 unit tests for classify_attachment_kind Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
Co-authored-by: Copilot <[email protected]>
…match Co-authored-by: chinkan <[email protected]> Agent-Logs-Url: https://github.com/chinkan/RustFox/sessions/9c502a20-dd1e-47bb-8b4d-34cf2f26c7f1
Switched OCR from tesseract to Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
There was a problem hiding this comment.
Pull request overview
Adds Telegram media (photos + documents) support by downloading attachments to temp storage, extracting usable text (OCR/PDF/DOCX) and/or sending images as multimodal content to vision-capable models, with long-document fallback to the existing knowledge/vector store.
Changes:
- Extend platform message model with typed attachments and add Telegram download + cleanup logic for photos/documents.
- Promote LLM
ChatMessage.contentto a backwards-compatible multimodal representation (text or parts). - Add a file processing pipeline (vision base64, OCR via
ocrs, PDF/DOCX extraction, long-context chunking + RAG retrieval) and wire it into the agent.
Reviewed changes
Copilot reviewed 17 out of 19 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| src/supervisor/backend/reasoning.rs | Populates new attachments field for supervisor-originated messages. |
| src/platform/telegram.rs | Downloads Telegram photos/documents into temp dirs, classifies attachments, cleans up after processing, adds unit tests. |
| src/platform/mod.rs | Introduces AttachmentKind, Attachment, and IncomingMessage.attachments. |
| src/memory/summarizer.rs | Adapts summarizer to new MessageContent and as_text() extraction. |
| src/memory/rag.rs | Updates RAG snippet building to use MessageContent::as_text(). |
| src/memory/query_rewriter.rs | Updates query rewriter to build prompts from MessageContent::as_text(). |
| src/memory/conversations.rs | Stores/retrieves text-only content while mapping DB rows to MessageContent::Text. |
| src/llm.rs | Adds multimodal content types (MessageContent, ContentPart) with backward-compatible serialization + tests. |
| src/lib.rs | Exposes new file_processor module. |
| src/learning.rs | Updates learning flows to use MessageContent consistently. |
| src/file_processor/mod.rs | New attachment processing module: vision/OCR, PDF/DOCX extraction, chunking + knowledge-store indexing/retrieval. |
| src/config.rs | Adds openrouter.supports_vision and [ocr].model_dir defaults + tests. |
| src/agent.rs | Integrates attachment processing into message construction and persistence behavior. |
| src/agent_prompt.rs | Adjusts prompt estimation/compaction logic for MessageContent. |
| docs/plans/2026-03-25-telegram-file-image-support.md | Adds an implementation plan documenting the intended architecture and dependencies. |
| config.example.toml | Documents new supports_vision and [ocr] settings. |
| Cargo.toml | Adds new crates for OCR/image/PDF/DOCX/mime/base64 support. |
| Cargo.lock | Locks new transitive dependencies for added crates. |
| .gitignore | Updates ignored DB filename pattern to rustfox.db*. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let dest_name = match filename { | ||
| Some(n) => n.to_string(), | ||
| None => format!("{}.{}", uuid::Uuid::new_v4(), ext), | ||
| }; | ||
| let dest_path = dest_dir.join(&dest_name); |
| let ext = Path::new(&file.path) | ||
| .extension() | ||
| .and_then(|e| e.to_str()) | ||
| .unwrap_or("bin"); | ||
|
|
| dest_dir: &Path, | ||
| filename: Option<&str>, | ||
| ) -> Result<(PathBuf, String)> { | ||
| std::fs::create_dir_all(dest_dir).context("Failed to create temp directory")?; |
| .await | ||
| .context("Failed to download file from Telegram")?; | ||
|
|
||
| std::fs::write(&dest_path, &bytes).context("Failed to write downloaded file")?; |
| let response = reqwest::get(url) | ||
| .await | ||
| .context("Failed to fetch OCR model")?; | ||
| let bytes = response | ||
| .bytes() | ||
| .await | ||
| .context("Failed to read OCR model bytes")?; |
| // Save a text-only version to DB (avoid storing base64 image data in message history) | ||
| let db_content = if incoming.attachments.is_empty() { | ||
| user_msg_content.clone() | ||
| } else { | ||
| let mut db_text = incoming.text.clone(); | ||
| if !attachment_text.is_empty() { | ||
| db_text.push_str("\n\n[Attachment processed]"); | ||
| } | ||
| MessageContent::from_text(db_text) | ||
| }; |
| # Used to extract text from images when supports_vision = false. | ||
| # Models are downloaded automatically on first use. | ||
| # [ocr] | ||
| # model_dir = "~/.cache/ocrs" # Where OCR model files are cached (downloaded on first use) |
| tokio::task::spawn_blocking(move || -> Result<String> { | ||
| let detection_model = | ||
| rten::Model::load_file(&det_path).context("Failed to load OCR detection model")?; | ||
| let recognition_model = | ||
| rten::Model::load_file(&rec_path).context("Failed to load OCR recognition model")?; |
| match extract_pdf_text(&attachment.path) { | ||
| Ok(text) => { | ||
| let ctx = handle_context_length(&text, fname, user_query, memory).await; | ||
| text_parts.push(ctx); | ||
| } | ||
| Err(e) => { | ||
| tracing::warn!("PDF extraction failed: {}", e); | ||
| text_parts.push(format!("[PDF processing failed: {}]", e)); | ||
| } | ||
| } |
| match extract_docx_text(&attachment.path) { | ||
| Ok(text) => { | ||
| let ctx = handle_context_length(&text, fname, user_query, memory).await; | ||
| text_parts.push(ctx); | ||
| } | ||
| Err(e) => { | ||
| tracing::warn!("DOCX extraction failed: {}", e); | ||
| text_parts.push(format!("[DOCX processing failed: {}]", e)); | ||
| } | ||
| } |
…rguments Switches compaction marker format from JSON to plain-text (COMPACTION_MARKER_PREFIX) and adds defensive detection in both the main agent loop and subagent tool dispatch. Also updates agent model references to opencode-go/* variants.
Split run_subagent into two paths:
- Ad-hoc mode (skill_name = None): use system_prompt + user_prompt
directly with a default sandbox tool whitelist and ambient system
context (timestamp, user model, location) auto-injected.
- Predefined mode: existing registry lookup, with new support for
skip_bootstrap and YAML frontmatter stripping from the agent body.
The shared mini-agentic loop (LLM call -> empty-response recovery ->
tool execution -> final response) is now extracted into a reusable
run_subagent_loop helper used by both paths. The 'Agent {}' log
prefixes are unified to 'Subagent {}' to match the new generic label.
The invoke_agent tool handler is updated to pass the new signature.
…fixes - Add spawn_agents tool for ad-hoc inline subagents (no AGENT.md needed) - Parallel execution: multiple subagent calls run concurrently via join_all - System context (date/time, user model, location) auto-injected into subagents - Remove deprecated invoke_subagent (use invoke_agent with skill fallback) - Add skip_bootstrap frontmatter flag for AGENT.md direct body injection - Add agents/verifier/AGENT.md with read-only sandbox access (read_file, list_files, plan_view) - Add Verification Protocol to system prompt (verify work before ending) - Add agent discovery notes to system prompt (DO NOT list agent directories) - Add SubagentsConfig with default_tools in config - Compaction: fix bytes vs chars naming, preserve image parts in tool results - Compaction: add second-pass hard cap (100KB) to prevent context overflow - Compaction: move prepare_messages_for_llm outside retry loops - Compaction: compact long assistant text-only messages - Compaction: fix marker wording (bytes not characters)
- Remove AgentKind enum (only Agent variant remained after invoke_subagent removal) - Remove kind parameter from run_subagent signature - Simplify match blocks to plain code blocks - Remove #[allow(dead_code)] from build_subagent_system_prompt (now used) - Add #![deny(dead_code)] to lib.rs to prevent future dead code
…ier, compaction - Add File & Image Support, Long-Context RAG, Long-Term Memory features - Add Ad-Hoc Subagents, Zero-Trust Verifier to feature list - Update agent tools table (add spawn_agents, remove invoke_subagent) - Add file_processor/ and agent_prompt.rs to architecture diagram - Add agents/verifier/ to directory tree - Update Roadmap with completed items
Prepares src/bin/setup.rs for the setup wizard redesign by introducing TOML parse structs that mirror the expanded config surface used by the main bot (agent, langsmith, embedding, ocr, learning, supervisor, subagents, skills, agents_config). - Add RawAgent, RawLangSmith, RawEmbedding, RawOcr, RawLearning, RawSupervisor (with RawSupervisorRisk), RawSubagents, RawSkills, RawAgentsConfig — all Clone, all fields optional - Extend RawConfig with the new optional sections - Extend RawOpenRouter with base_url + supports_vision - Extend RawMemory with query_rewriter_enabled - Extend RawGeneral with home (consumed by load_config) - Extend ExistingConfig with the 19 new fields surfaced to the wizard - Wire load_config to populate the new fields from parsed sections CI-clean: cargo fmt --check, cargo clippy -- -D warnings, and all 15 existing tests pass.
Restructure the setup wizard from 7 flat steps to 4 wizard steps
plus a success page, with collapsible Advanced sections and a global
'Show all settings' toggle. This is Task 3 of the wizard redesign
(see docs/superpowers/plans/2026-06-11-setup-wizard-redesign.md).
Step layout:
1. Bot & LLM Setup — required bot token, user IDs, OR key
+ Advanced: model, prompt, vision, etc.
2. Location & Storage — location, sandbox/db/skills/agents dirs
+ Advanced: query rewriting, agent loop
3. Integrations — MCP catalog (moved from step 5)
+ Advanced: LangSmith, Embedding, Learning
4. Review & Save — summary of all settings + raw TOML preview
5. Success — done page
State, validation, collect, loadExistingConfig, and generateToml all
extended to cover the new fields (supports_vision, base_url, skills_dir,
agents_dir, home_dir, ocr_model_dir, agent_max_iterations, agent_empty_
response_retry_limit, langsmith_*, embedding_*, query_rewriter_enabled,
learning_*).
Preserved verbatim:
- Dark theme CSS, MCP_CATALOG (18 servers), OAuth popup flow
(Notion + Google Workspace + Threads modals), esc() helper,
TOML output for all [[mcp_servers]] sections, /api/save-config
and /api/load-config endpoints.
…th resolution - Rewrite setup/index.html with progressive disclosure (4 steps + success) - Add global 'Show all settings' toggle for tech users - Add collapsible advanced sections with inline validation - Add syncStateToForm() to load existing config into wizard fields - Resolve config path from RUSTFOX_HOME → RUSTFOX_ROOT → ~/.rustfox → cwd - Add --advanced flag to CLI wizard for full settings control - Extend ConfigParams/format_config with all config sections - Remove sandbox.allowed_directory from wizard (auto-resolved to workspace) - Add Raw* parse structs for all config sections - Add 24 new tests for format_config sections - Add spec and plan documents
Features
Telegram File/Image Support
Telegram only supported text messages. This adds handling for photos and file attachments (PDF, DOCX, images), routing them through a vision/OCR/document extraction pipeline before injecting context into the LLM.
IncomingMessagegainsattachments: Vec<Attachment>telegram.rshandlesmsg.photo()andmsg.document()with per-request temp dirsChatMessage.contentpromoted fromOption<String>toMessageContent(Text or Parts for multi-modal)ocrs(pure Rust, no system deps)pdf-extractdocx-rsEmbeddingEngine+sqlite-vec, RAG-retrieved per queryLong-Term Memory & Startup/Shutdown
/clearnow archives (searchable but excluded from active context)McpManagergainsserver_count()for statusParallel Ad-Hoc Subagents
spawn_agentstool: spawn subagents with inline system prompts — no AGENT.md neededspawn_agents(tasks=[{system_prompt, prompt}, ...])— parallel batch viatokio::join_allspawn_agentsorinvoke_agentcalls in one LLM response run concurrentlyinvoke_subagent— useinvoke_agentwithskillfallbackZero-Trust Verifier
agents/verifier/AGENT.mdwithskip_bootstrap: trueinvoke_agent(agent="verifier", prompt="TASK:...\nCRITERIA:...\nEVIDENCE:...")read_file,list_files,plan_view— no write tools<evaluation>PASS/NEEDS_IMPROVEMENT/FAIL</evaluation>list_filesskip_bootstrapFrontmatterskip_bootstrap: trueflag in AGENT.md/SKILL.md frontmatterContext Compaction Improvements
compact_tool_resultnow preservesMessageContent::Parts(image parts survive)prepare_messages_for_llmmoved outside retry loops (compaction is invariant)estimate_prompt_chars→estimate_prompt_bytes(measures bytes, not chars)"characters"→"bytes"in compacted markersCode Quality
AgentKindenum (onlyAgentvariant remained)#![deny(dead_code)]to lib.rs to prevent future dead codeNew Dependencies
ocrsrtenimagepdf-extractdocx-rsinferbase64