Spaces:
Sleeping
Sleeping
Commit History
-
Add live progress panel and Stop button to the Gradio app
The chat handler is now a generator that streams progress events as the workflow runs. Each agent and tool gets wrapped (idempotently, at init time) to push a "starting…" / "done" line to a per-turn ProgressBus and to check a cooperative-stop flag at entry. The Coach's line is enriched with the chosen action so the panel reads like: ⏳ Coach: starting… ✓ Coach → MedicalAssessmentAgent: assess profile… ⏳ Medical Assessment: starting… └─ WebSearchTool: running… └─ WebSearchTool: done ✓ Medical Assessment: done … ✅ Done. The Stop button flips the bus flag. We can't kill an in-flight Gemini HTTP call, but the wrappers raise StopRequested on the next agent / tool boundary and the workflow unwinds cleanly with a "stopped by user" message in the chat. Gradio's ``cancels=`` is wired as a best-effort extra (older Gradio versions ignore it).6b8bf73 -
Simplify topology to 3 agents + 2 tools
Folded the Validator's deterministic layer into the Planner (post-LP check_plan with up to 2 internal revisions) and its LLM-graded layer into the Coach's self-review. Removed the standalone KnowledgeAgent — the WebSearchTool now uses Gemini's grounded google_search natively, which returns source URIs in grounding_metadata. Dropped the LLM-driven ComputationTool / AST sandbox; the MedicalAssessmentAgent now runs nutrition_formulas.full_assessment() directly and overwrites the LLM's calculations with the deterministic result. Tightened the Coach decision schema: call_tool is no longer a valid action, PlannerToolName is a strict Literal of {WebSearchTool, QuantitiesFinder}, MedicalAssessmentDecision drops tool_name. The QuantitiesFinder LP gets default per-food bounds (min = max(20, est*0.3), max = min(400, est*2.5)) and the estimate-anchor weight bumps 0.1 -> 0.3 so the solver stops drifting into 1 g of butter / 900 g of broccoli. End-to-end audit against a real Gemini key: Coach -> M…1933348
-
Rename module to mealgraph; add CC BY-NC 4.0 LICENSE; slim Space metadata
- Rename Python module from nutritionmas to mealgraph (file, imports, env prefix MEALGRAPH_, logger namespace, pyproject project name); README and examples updated to match. - LICENSE: Creative Commons Attribution-NonCommercial 4.0 International. Space metadata `license:` switched to cc-by-nc-4.0. - Space front-matter trimmed: drop colorFrom, colorTo, pinned. README header shows MealGraph as the project name.e28d52e -
Add live demo link to README
8ed766d -
Harden Gemini structured-decoding contract and write_memory semantics
- utils: add pydantic_to_gemini_schema sanitizer so response_schema accepts the Pydantic models (strip additionalProperties / $ref / $defs / title / default; collapse anyOf-with-null into nullable). Unwrap single-element list responses in call_typed. - schemas: declare json_schema_extra property hints on CoachDecision.params and Planner draft/final plan fields (with required `items` on arrays); infer missing action_type from populated discriminator fields on both MedicalAssessmentDecision and PlannerDecision. - tools: anchor ComputationTool's free-form unit detection to the position immediately after each number with a non-word, non-slash terminator; compose partial assessments for BMR/TDEE/calorie-target requests. - agents: reject empty final_plan envelopes and force the Planner to re-iterate so the Validator never sees an empty shell. - workflow: merge semantics for top-level write_memory partitions so a partial Coach update preserves existing keys. - docs: rewrit…9918f43
-
Switch to Gemini 3.x latest aliases; grounded WebSearch; lab results in UI
- Models: default configs and Gradio dropdowns now use the rolling gemini-pro-latest / gemini-flash-latest / gemini-flash-lite-latest aliases (Gemini 3.x family). Rate-limit table rekeyed accordingly; the dead gemma-3-27b-it slot is removed. - WebSearchTool collapsed to a single Gemini call with the built-in google_search tool. GeminiLLM.call_grounded() returns (text, citations, queries) from grounding_metadata; the tool just appends a Sources block. DDGS / search_web_raw / the two-pass query + synthesis loop are gone, ddgs dropped from requirements.txt and pyproject.toml. - Gradio profile now exposes a Lab results textbox that flows into memory.medical_history.lab_results. - README and tech_documentation updated for the new model family and grounded search; test_app fixture extended for the new field. All 84 tests pass.3fb99da -
Bump HF Space SDK to gradio 5.50.0; drop dev deps from requirements
The previous build failed because HF Space pinned gradio[oauth]==5.0.0 (via README's sdk_version: 5.0.0). That Oct-2024 gradio carries old transitive constraints (pillow<11, markupsafe~=2.0, tomlkit==0.12.0) that caused pip to backtrack heavily on Python 3.13 and ultimately try to source-build pillow 10.3.0 — which has no cp313 wheel and crashes with KeyError: '__version__'. Moving to gradio 5.50.0 brings constraints that resolve cleanly on 3.13. Dev tools (pytest, pytest-asyncio, pytest-cov, ruff) are also removed from requirements.txt — they live in pyproject.toml's `dev` extra and were adding resolver noise on every Space build.e707329 -
Drop ddgs <7 cap so HF Space (Python 3.13) can resolve a version
PyPI for Python 3.13 only publishes ddgs 9.x — the old <7 pin made the resolver fail. The DDGS().text() call site in tools.py uses fields that are unchanged across 6.x→9.x.f4af757
-
Shorten short_description to satisfy HF Space 60-char limit
c6e1d81 -
Phase 7: Gradio demo + Hugging Face Space metadata
app.py - the entry point for the Hugging Face Space. * Sidebar: - paste Gemini API keys (one per line) + per-role model overrides for Coach / Workers / Tools+Validator - rate-limit + debug toggles - "Initialize system" button -> spins up create_llm_instances / initialize_tools / initialize_agents / setup_workflow / initialize_long_term_memory + init_langsmith() - user-profile form (name, age, sex, anthropometrics, goal, allergies, dislikes, country, conditions, meds) -> JSON state forwarded into the LangGraph memory on each turn * Main pane: - Chatbot (messages format, Gradio 5/6 compatible) - "Agent activity" accordion: per-call _BufferHandler captures every nutrition_mas.* log line so the user can see Coach -> Medical -> Validator -> Planner unfold in real time - "System metrics" accordion: live MetricsCollector + ParseMetrics snapshot rendered as a markdown table - "Agent registry" accordion: pretty-printed AgentCard list from agen…ba71b93 -
Merge Phase 6 into Phase 7 base
bc3c906 -
Phase 6: Observability + offline eval harness
observability.py * init_langsmith() honours the standard LANGCHAIN_TRACING_V2 / LANGCHAIN_API_KEY env vars; off by default; logs a one-liner if on so the user knows tracing is live. * MetricsCollector singleton: per-agent and per-tool {calls, total_seconds, last_seconds, errors}. snapshot() also folds in the Phase 1 ParseMetrics (native vs fallback vs failure parses, by model). Phase 7's UI renders this as a live system-health pane. * span(label, kind='agent'|'tool'|'misc') context manager: times a block, records into the collector, emits a ⏱ debug line. evals/ * fixtures.py: three personas tuned to exercise the main risk axes: - Athlete -> macro_split protein floor (≥ 1.6 g/kg). - Diabetic -> requires_human_review propagation, calorie ceiling. - Vegan budget -> exclusions (peanut allergy, vegan), regional cues. * runner.py: walks each fixture WITHOUT calling Gemini -- builds the assessment via nutrition_formulas, hand-builds a plan that hits target, runs the …1ea0743 -
Merge Phase 5 into Phase 6 base
7b2ed64 -
Phase 5: A2A-style agent cards + MCP server stub for tools
agent_cards.py * AgentCard, Capability, CapabilityIO Pydantic models -- the data the emerging Agent-to-Agent (A2A) spec standardises. * AgentRegistry: in-process; future phase serves it at /.well-known/agent-card.json for cross-system discovery. * default_cards() ships one card per agent in this system. Medical's card sets requires_human_review=True so the Validator/Coach can route to HITL deterministically rather than via prompt heuristics. mcp_server.py * Wraps QuantitiesFinder, deterministic ComputationTool, safe_math_eval, and full_assessment as Model Context Protocol tools. * Uses the official `mcp` Python SDK as an OPTIONAL dependency -- not in requirements.txt; pip-installable on demand. Lazy import means the demo ships without it. * `python -m mcp_server` -> stdio transport; doc-comment shows the Claude Desktop / Cursor configuration snippet. tests/test_agent_cards.py (5 tests) * Default cards cover all 5 agents; registry register/get/by_role round- trips; M…c05050c -
Merge Phase 4 into Phase 5 base
dfe1f43 -
Phase 4: Close RCE in ComputationTool + guardrails (PII, prompt injection, HITL)
The original ComputationTool wrote LLM-generated Python to a temp file and ran it under subprocess.run(['python', ...]) on the host, with no sandbox. That was a remote-code-execution vector for any prompt-injected input - the most serious issue flagged in the Phase 0 architecture review. Phase 4 removes the subprocess path entirely. nutrition_formulas.py (NEW) * Closed-form clinical formulas: BMI, Mifflin-St Jeor BMR, TDEE (ACSM activity multipliers), goal-aware daily_calorie_target, macro_split with a 1.2-1.6 g/kg protein floor. * full_assessment() returns the same dict the Medical agent stored in memory.flags_and_assessments.calculations - drop-in replacement. tools.py - ComputationTool rewritten * New structured-op API: {"op": "bmi"|"bmr"|"tdee"|"calorie_target"| "macro_split"|"full_assessment"|"eval", ...args}. * Free-form English fallback: regex-extracts {weight_kg, height_cm, age} with unit hints, lexicon-matches sex/activity/goal, dispatches to the formula. If par…8805da9 -
Merge Phase 3 into Phase 4 base
6959626 -
Phase 3: SQLite long-term memory + KnowledgeAgent seam
Adds the three-tier memory taxonomy from the 2026 agent-memory literature (working / episodic / semantic / procedural) backed by stdlib sqlite3 - zero new deps. Mirrors Mem0's interface so a future swap is one-line. memory.py * LongTermMemory(db_path=None) -- in-memory by default, file for persistence. * Three tables, all keyed by user_id: - semantic_facts(fact_type, content, source) -- likes/dislikes/allergies - procedural_records(plan_summary, verdict, issues_json) -- validator history - episodic_sessions(session_id, payload_json) -- full snapshot for replay * Filtered recall by fact_type / substring / limit; forget_fact() to support user-driven correction. knowledge.py * KnowledgeAgent.handle_task(task, memory) returns JSON {answer, citations}. * Default backing is WebSearchTool with per-kind query biasing toward authoritative domains (USDA FDC for nutrition, WHO/diabetes.org/EFSA/NICE for guidelines, MedlinePlus/FDA/NIH for drug interactions). * Always emits a citatio…6cdcdeb -
Merge Phase 2 into Phase 3 base
c504fac -
Phase 2: ValidationAgent (critic loop) + Coach orchestrates revisions
Adds the ValidationAgent that the original README promised but never shipped. Implements the generator-critic pattern that Anthropic's multi-agent research-system writeup credits with much of its >90% lift over single-agent baselines. New module: validation.py * ValidationAgent.handle_task(task, memory) -> JSON ValidationDecision. * Two-layer review: - DETERMINISTIC (no LLM, instant, tested): * allergy violation -> severity high (auto-reject, never call LLM) * disliked food -> severity low (advisory) * calorie deviation > 3% of target -> severity medium * each macro deviation > 5% of target -> severity medium - LLM-GRADED (one Gemini Flash round-trip via call_typed): * medical-flag respect (e.g., diabetes user shouldn't see high-GL items) * citation presence for clinical recommendations * cultural appropriateness against user_profile.country * Verdict semantics: any high -> reject, any medium -> revise, else pass. * Force requires_human_review=true…34b037e -
Merge Phase 1 into Phase 2 base
1a5356f -
Phase 1: Pydantic schemas + Gemini response_schema (constrained decoding)
Replaces JSON-in-text with provider-native structured output for every agent decision. The legacy regex/json_repair path is now a measured fallback (ParseMetrics) — goal in Phase 2 evals is fallback_parses == 0. New module: schemas.py * CoachDecision, MedicalAssessmentDecision (+ MedicalAssessmentResult, Calculations, MacroTargets), PlannerDecision (+ FinalPlan, FoodItem), ResponseStep, ValidationDecision (declared early; used in Phase 2). * Outer envelope strict (action/action_type literals, numeric bounds); per-action params kept as dicts to dodge Gemini's discriminated-union schema quirks. Phase 2 will tighten further. utils.py * GeminiLLM gains call_typed(prompt, response_model) -> ParsedModel|None. Internally uses response_mime_type=application/json + response_schema= PydanticClass; non-streaming path so response.parsed is populated. * Graceful fallback chain: response.parsed -> dict validate -> extract_and_parse_json -> failure (each branch increments a per-model …88245f7 -
Phase 0: Foundations cleanup and test scaffold
Stop the bleeding before the larger refactor: * Add pyproject.toml + requirements.txt with pinned deps; .env.example, .gitignore, Makefile. * Replace mutable config.py module-globals with a Pydantic-Settings singleton (Settings/get_settings/set_settings/reset_settings). PEP 562 __getattr__ preserves backwards compatibility for existing 'config.DEBUG_MODE'-style reads; writes are routed through set_settings() so the singleton cannot be shadowed. * Add logging_setup.py: namespaced 'nutrition_mas.*' loggers, idempotent configure_logging(), refresh_level() to react to debug toggles. * utils.py cleanup: - delete the duplicate GeminiLLM class (the second definition silently shadowed the first) - drop 'from google.colab import userdata' so the module imports outside Colab - route prints through the structured logger - keep extract_and_parse_json as the parser (Phase 1 will demote it to a measured fallback once native response_schema lands) * nutritionmas.py: …f8381b8 -
Initial commit: existing Nutrition MAS prototype
bcd961e