Skip to content

feat(rag): add Ollama embeddings + Gemini 2.5 agent support - #59

Merged
w7-mgfcode merged 1 commit into
mainfrom
dev
Feb 2, 2026
Merged

feat(rag): add Ollama embeddings + Gemini 2.5 agent support#59
w7-mgfcode merged 1 commit into
mainfrom
dev

Conversation

@w7-mgfcode

@w7-mgfcode w7-mgfcode commented Feb 2, 2026

Copy link
Copy Markdown
Owner

Summary

  • Ollama embedding provider: Local/LAN RAG indexing with models like nomic-embed-text
  • Gemini 2.5 agent support: Extended reasoning (thinking budget) for complex planning tasks
  • API key environment handling: Only set env vars if not already present
  • RAG threshold improvements: Local variable for effective threshold, settings fallback
  • JSON serialization: Fallback str() for unknown types in message serialization

Changes

  • app/features/agents/agents/base.py - API key validation improvements
  • app/features/agents/agents/rag_assistant.py - Better error handling
  • app/features/agents/service.py - JSON serialization robustness
  • app/features/rag/service.py - Threshold handling refactor
  • app/features/rag/schemas.py - Schema refinements
  • docs/rag-ollama-setup.md - Comprehensive Ollama setup guide (742 lines)

Test plan

  • All CI checks passing on dev
  • Unit tests updated for PydanticAI v1.48 API
  • RAG schema tests updated for None default threshold

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • RAG similarity threshold is now configurable via application settings instead of hardcoded.
    • Added comprehensive setup documentation for Ollama + Gemini integration.
  • Bug Fixes

    • Improved API key handling for multiple AI providers.
    • Fixed DateTime serialization for message processing.
    • Updated compatibility with latest library versions.
  • Documentation

    • New bilingual setup guide (English/Hungarian) for RAG system configuration with local embeddings and Gemini agents, including troubleshooting steps.

✏️ Tip: You can customize this high-level summary in your review settings.

* feat(rag): configure Ollama embeddings + Gemini 2.5 agents

- Add os.environ export for API keys in validate_api_key_for_model
- Wrap model_settings for PydanticAI Agent constructor
- Fix result.output (PydanticAI v1.48.0 API change)
- Add datetime serialization in _serialize_messages
- Apply RAG_SIMILARITY_THRESHOLD from settings (configurable default)
- Make similarity_threshold nullable in schemas
- Add bilingual (EN/HU) setup documentation

Tested with:
- Ollama qwen3-embedding:4b (10.0.0.226:11434)
- Google Gemini 2.5 Pro via google-gla provider

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix(agents): correct test mocks and type annotations

- Fix tests to use .output instead of .data (PydanticAI v1.48)
- Replace Any with object in json_safe function (ANN401)
- Handle None threshold in RAG service retrieve method

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* refactor: address code review feedback

- Only set API key env vars if not already present (avoids repeated mutations)
- Use local variable for effective threshold in RAG retrieve (clearer intent)
- Add fallback str() conversion in json_safe for unknown types

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* test(rag): update test for None default threshold

Schema defaults similarity_threshold to None so service can use
settings fallback. Updated test to match.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Gabe@w7dev <gabor@w7-7.net>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Feb 2, 2026

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

Configuration-driven similarity thresholds replace hardcoded defaults in RAG services. Agent model configuration wraps settings under a "model_settings" key for PydanticAI compatibility. API keys are exported to environment variables. Result attribute changed from data to output for PydanticAI v1.48.0. Datetime serialization support added for message handling. Comprehensive setup guide added.

Changes

Cohort / File(s) Summary
Agent Configuration & API Keys
app/features/agents/agents/base.py
Environment variable exports for API keys in validate_api_key_for_model. Model settings wrapped under top-level "model_settings" key. Imports added for os module.
RAG Similarity Threshold Configuration
app/features/rag/schemas.py, app/features/rag/routes.py, app/features/rag/service.py, app/features/agents/agents/rag_assistant.py
Similarity threshold refactored from hardcoded 0.7 to configurable default via settings. Field type changed to Optional[float] with None default. Runtime logic applies settings fallback when threshold not provided.
Agent Service & Result Handling
app/features/agents/service.py, app/features/agents/tests/test_routes.py, app/features/agents/tests/test_service.py
Result attribute updated from result.data to result.output for PydanticAI v1.48.0 compatibility. Added json_safe helper for datetime serialization in messages. Updated tests to match new result attribute.
RAG Schema Tests
app/features/rag/tests/test_schemas.py
Default similarity_threshold expectation changed from 0.7 to None, reflecting settings-based configuration.
Documentation
docs/rag-ollama-setup.md
Comprehensive bilingual setup guide for local Ollama + Gemini 2.5 RAG system. Covers architecture, configuration, code adjustments, testing procedures, troubleshooting, and best practices.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 Through settings we hop, no hardcoded chains,
API keys safe in environment lanes,
PydanticAI blessed with proper wrapping joy,
Datetime strings purified, no serialization ploy,
RAG thresholds dance with configuration's might!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main features added: Ollama embeddings support and Gemini 2.5 agent integration, which are the primary focus of the changeset across agent and RAG modules.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry @w7-mgfcode, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@w7-mgfcode
w7-mgfcode merged commit d822742 into main Feb 2, 2026
11 of 12 checks passed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@app/features/agents/agents/base.py`:
- Around line 71-92: The env-var presence checks in the provider branch
(provider == "anthropic", "openai", "google-gla"/"google-vertex") only test for
key existence in os.environ and therefore treat an existing empty string as
valid; change the logic to treat empty strings as missing by checking both
presence and non-empty value (e.g., if os.environ.get("ANTHROPIC_API_KEY") not
truthy) before skipping setting, and when settings.anthropic_api_key /
settings.openai_api_key / settings.google_api_key are provided, set
os.environ[...] to that value if the current env var is missing or empty so
downstream clients see the real key (apply the same pattern for
ANTHROPIC_API_KEY, OPENAI_API_KEY, and GOOGLE_API_KEY).

In `@app/features/agents/service.py`:
- Around line 266-267: In stream_chat(), the code awaits result.get_data() but
PydanticAI v1.48.0's StreamedRunResult exposes get_output() instead; update the
call in the stream_chat function to await result.get_output() (matching the
non-streaming chat() use of get_output()), ensuring you reference the
StreamedRunResult instance named result and replace get_data() with get_output()
so the final validated output is retrieved correctly.

In `@docs/rag-ollama-setup.md`:
- Around line 508-530: The documentation snippet for validate_api_key_for_model
does not show the environment-guard present in the real implementation; update
the docs to wrap each os.environ assignment with a check like if
"ANTHROPIC_API_KEY" not in os.environ (and similarly for OPENAI_API_KEY and
GOOGLE_API_KEY) so the function validate_api_key_for_model only sets
os.environ["ANTHROPIC_API_KEY"], os.environ["OPENAI_API_KEY"], or
os.environ["GOOGLE_API_KEY"] when the respective setting
(settings.anthropic_api_key, settings.openai_api_key, settings.google_api_key)
is present and the environment variable is not already set, matching the actual
code behavior.
🧹 Nitpick comments (2)
docs/rag-ollama-setup.md (2)

1-50: Well-structured bilingual documentation.

The overview and architecture sections effectively document the RAG system with Ollama embeddings. The ASCII architecture diagrams clearly illustrate the component relationships.

Consider adding a language specifier to the architecture diagram code block for consistency:

📝 Minor: Add language specifier to architecture diagram
-```
+```text
 ┌─────────────────┐     ┌─────────────────┐     ┌─────────────────┐
 │  ForecastLabAI  │────▶│  Ollama Server  │────▶│  qwen3-embedding │

346-370: Optional: Add language specifiers to error message code blocks.

Static analysis flags missing language specifiers on error message blocks. While minor, adding specifiers like text or plaintext improves consistency.

📝 Example fix
-```
+```text
 Failed to connect to Ollama at http://10.0.0.226:11434
</details>

</blockquote></details>

</blockquote></details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

Comment on lines +71 to +92
if provider == "anthropic":
if not settings.anthropic_api_key:
raise ValueError(
"Anthropic API key not configured. Set ANTHROPIC_API_KEY environment variable."
)
# Only set env var if not already present to avoid repeated mutations
if "ANTHROPIC_API_KEY" not in os.environ:
os.environ["ANTHROPIC_API_KEY"] = settings.anthropic_api_key
elif provider == "openai":
if not settings.openai_api_key:
raise ValueError(
"OpenAI API key not configured. Set OPENAI_API_KEY environment variable."
)
if "OPENAI_API_KEY" not in os.environ:
os.environ["OPENAI_API_KEY"] = settings.openai_api_key
elif provider in ["google-gla", "google-vertex"]:
if not settings.google_api_key:
raise ValueError(
"Google API key not configured. Set GOOGLE_API_KEY environment variable."
)
if "GOOGLE_API_KEY" not in os.environ:
os.environ["GOOGLE_API_KEY"] = settings.google_api_key

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Handle empty env-var values when exporting API keys.

If the env var exists but is an empty string, the code will skip setting the real key and downstream clients will still see a blank value. Consider treating empty as “missing.”

🛠️ Proposed fix
-        if "ANTHROPIC_API_KEY" not in os.environ:
+        if not os.environ.get("ANTHROPIC_API_KEY"):
             os.environ["ANTHROPIC_API_KEY"] = settings.anthropic_api_key
@@
-        if "OPENAI_API_KEY" not in os.environ:
+        if not os.environ.get("OPENAI_API_KEY"):
             os.environ["OPENAI_API_KEY"] = settings.openai_api_key
@@
-        if "GOOGLE_API_KEY" not in os.environ:
+        if not os.environ.get("GOOGLE_API_KEY"):
             os.environ["GOOGLE_API_KEY"] = settings.google_api_key
🤖 Prompt for AI Agents
In `@app/features/agents/agents/base.py` around lines 71 - 92, The env-var
presence checks in the provider branch (provider == "anthropic", "openai",
"google-gla"/"google-vertex") only test for key existence in os.environ and
therefore treat an existing empty string as valid; change the logic to treat
empty strings as missing by checking both presence and non-empty value (e.g., if
os.environ.get("ANTHROPIC_API_KEY") not truthy) before skipping setting, and
when settings.anthropic_api_key / settings.openai_api_key /
settings.google_api_key are provided, set os.environ[...] to that value if the
current env var is missing or empty so downstream clients see the real key
(apply the same pattern for ANTHROPIC_API_KEY, OPENAI_API_KEY, and
GOOGLE_API_KEY).

Comment on lines +266 to +267
# NOTE: PydanticAI v1.48.0 uses result.output (not result.data)
result_data: Any = result.output

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

# First, find the stream_chat implementation in service.py
grep -n "stream_chat\|get_data\|result\.output" app/features/agents/service.py | head -20

Repository: w7-mgfcode/ForecastLabAI

Length of output: 408


🏁 Script executed:

# Check the dependencies to confirm PydanticAI version
fd -e "requirements.txt" -e "pyproject.toml" -e "poetry.lock" -e "setup.py" | head -5 | xargs grep -l "pydantic" 2>/dev/null

Repository: w7-mgfcode/ForecastLabAI

Length of output: 52


🌐 Web query:

PydanticAI v1.48.0 run_stream API result object output get_data method documentation

💡 Result:

In PydanticAI (pydantic-ai) v1.48.0, the object you get from Agent.run_stream(...) is a StreamedRunResult, and the method to “get the data” is get_output() (not get_data). (ai.pydantic.dev)

What to use in v1.48.0

  • Async streaming run:

    • async with agent.run_stream(...) as result:
    • Stream partial output via result.stream_text(...) / result.stream_output(...)
    • Then get the final validated output via await result.get_output() (ai.pydantic.dev)
  • Sync streaming run:

    • agent.run_stream_sync(...) returns a sync wrapper (StreamedRunResultSync)
    • Then call result.get_output() (sync) (ai.pydantic.dev)

Where it’s documented

  • API reference for StreamedRunResult.get_output() (“Stream the whole response, validate and return it.”) (ai.pydantic.dev)
  • Core concept docs explaining that run_stream() returns a streamed result wrapper around the final output (ai.pydantic.dev)

About get_data()

If you’re seeing get_data() in older/community mirrors (or older docs snapshots), that name doesn’t match the current official v1.48.0 API; in v1.48.0 the corresponding method is get_output(). (ai.pydantic.dev)

Citations:


Replace result.get_data() with result.get_output() in stream_chat().

In PydanticAI v1.48.0, StreamedRunResult uses get_output() (not get_data()) to retrieve the final validated output. Line 417 currently calls await result.get_data(), which will fail at runtime. Update to await result.get_output() to match the corrected API and maintain consistency with the non-streaming chat() method.

🤖 Prompt for AI Agents
In `@app/features/agents/service.py` around lines 266 - 267, In stream_chat(), the
code awaits result.get_data() but PydanticAI v1.48.0's StreamedRunResult exposes
get_output() instead; update the call in the stream_chat function to await
result.get_output() (matching the non-streaming chat() use of get_output()),
ensuring you reference the StreamedRunResult instance named result and replace
get_data() with get_output() so the final validated output is retrieved
correctly.

Comment thread docs/rag-ollama-setup.md
Comment on lines +508 to +530
**File: `app/features/agents/agents/base.py`**
```python
import os
from app.core.config import get_settings

def validate_api_key_for_model(model: str) -> None:
"""Validate and export API key to environment."""
settings = get_settings()
provider = model.split(":")[0]

if provider == "anthropic":
if not settings.anthropic_api_key:
raise ValueError("ANTHROPIC_API_KEY not configured")
os.environ["ANTHROPIC_API_KEY"] = settings.anthropic_api_key
elif provider == "openai":
if not settings.openai_api_key:
raise ValueError("OPENAI_API_KEY not configured")
os.environ["OPENAI_API_KEY"] = settings.openai_api_key
elif provider in ["google-gla", "google-vertex"]:
if not settings.google_api_key:
raise ValueError("GOOGLE_API_KEY not configured")
os.environ["GOOGLE_API_KEY"] = settings.google_api_key
```

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "base.py" -path "*/agents/agents/*" | head -5

Repository: w7-mgfcode/ForecastLabAI

Length of output: 104


🏁 Script executed:

cat -n app/features/agents/agents/base.py

Repository: w7-mgfcode/ForecastLabAI

Length of output: 5545


Update documentation snippet to include the environment variable guard.

The actual implementation correctly includes a guard (if "KEY_NAME" not in os.environ:) before setting API keys, as shown at lines 77-78, 84-85, and 91-92 of app/features/agents/agents/base.py. The documentation snippet at lines 508-530 of docs/rag-ollama-setup.md should be updated to reflect this optimization and match the actual implementation. Each API key assignment should be wrapped with a conditional check to avoid unnecessary mutations on repeated calls.

🤖 Prompt for AI Agents
In `@docs/rag-ollama-setup.md` around lines 508 - 530, The documentation snippet
for validate_api_key_for_model does not show the environment-guard present in
the real implementation; update the docs to wrap each os.environ assignment with
a check like if "ANTHROPIC_API_KEY" not in os.environ (and similarly for
OPENAI_API_KEY and GOOGLE_API_KEY) so the function validate_api_key_for_model
only sets os.environ["ANTHROPIC_API_KEY"], os.environ["OPENAI_API_KEY"], or
os.environ["GOOGLE_API_KEY"] when the respective setting
(settings.anthropic_api_key, settings.openai_api_key, settings.google_api_key)
is present and the environment variable is not already set, matching the actual
code behavior.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants