fix: Ollama message sanitization and streaming think slugs
Build and Push / build (release) Successful in 52s

- Fix `invalid message content type: <nil>` error from Ollama
- Create TatlockOllamaProvider that sanitizes messages (null → "")
- Update all agents to use sanitized provider
- Fix repeating think messages by adding ReasoningSummaryDone signal

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2025-12-16 00:19:03 +01:00
co-authored by Claude Opus 4.5
parent 74f47097c2
commit d5e5fc1ad8
8 changed files with 173 additions and 32 deletions
+4 -7
View File
@@ -102,18 +102,15 @@ _biographer_agent: Optional[Agent[None, str]] = None
def _create_biographer_agent() -> Agent[None, str]:
"""Create The Biographer PydanticAI agent."""
# Import required classes for Ollama configuration
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
# PydanticAI expects Ollama base URL to end with /v1
clean_host = str(config.OLLAMA_HOST).rstrip('/')
base_url = f"{clean_host}/v1"
from src.ollama.provider import get_ollama_provider
# Create Ollama model with provider
# Create Ollama model with sanitized provider
# (fixes 'content: null' issue with tool calls)
model = OpenAIChatModel(
model_name=config.OLLAMA_DEFAULT_MODEL,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider(),
)
agent: Agent[None, str] = Agent(
+4 -7
View File
@@ -113,18 +113,15 @@ _housekeeper_agent: Optional[Agent[None, str]] = None
def _create_housekeeper_agent() -> Agent[None, str]:
"""Create the Housekeeper PydanticAI agent."""
# Import required classes for Ollama configuration
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
# PydanticAI expects Ollama base URL to end with /v1
clean_host = str(config.OLLAMA_HOST).rstrip("/")
base_url = f"{clean_host}/v1"
from src.ollama.provider import get_ollama_provider
# Create Ollama model with provider
# Create Ollama model with sanitized provider
# (fixes 'content: null' issue with tool calls)
model = OpenAIChatModel(
model_name=config.OLLAMA_DEFAULT_MODEL,
provider=OllamaProvider(base_url=base_url),
provider=get_ollama_provider(),
)
agent: Agent[None, str] = Agent(
+4 -7
View File
@@ -122,18 +122,15 @@ _librarian_agent: Optional[Agent[None, str]] = None
def _create_librarian_agent() -> Agent[None, str]:
"""Create the Librarian PydanticAI agent."""
# Import required classes for Ollama configuration
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
# PydanticAI expects Ollama base URL to end with /v1
clean_host = str(config.OLLAMA_HOST).rstrip('/')
base_url = f"{clean_host}/v1"
from src.ollama.provider import get_ollama_provider
# Create Ollama model with provider
# Create Ollama model with sanitized provider
# (fixes 'content: null' issue with tool calls)
model = OpenAIChatModel(
model_name=config.OLLAMA_DEFAULT_MODEL,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider(),
)
agent: Agent[None, str] = Agent(
+10 -10
View File
@@ -143,7 +143,7 @@ class TatlockAgent(AgentInterface):
# Import required classes for Ollama configuration
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
from src.ollama.provider import get_ollama_provider
# PydanticAI expects Ollama base URL to end with /v1
# Remove trailing slash from ollama_host if present
@@ -153,7 +153,7 @@ class TatlockAgent(AgentInterface):
# Create Ollama model with provider
ollama_model = OpenAIChatModel(
model_name=self.model_name,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider()
)
# Create PydanticAI agent with Ollama model
@@ -448,7 +448,7 @@ class TatlockAgent(AgentInterface):
... )
"""
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
from src.ollama.provider import get_ollama_provider
logger.info(
"tatlock_run_with_scoped_tools",
@@ -464,7 +464,7 @@ class TatlockAgent(AgentInterface):
ollama_model = OpenAIChatModel(
model_name=self.model_name,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider()
)
# Create agent with scoped tools
@@ -542,7 +542,7 @@ class TatlockAgent(AgentInterface):
Text chunks from the streaming response
"""
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
from src.ollama.provider import get_ollama_provider
logger.info(
"tatlock_run_with_scoped_tools_stream",
@@ -557,7 +557,7 @@ class TatlockAgent(AgentInterface):
ollama_model = OpenAIChatModel(
model_name=self.model_name,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider()
)
# Create agent with scoped tools
@@ -637,7 +637,7 @@ class TatlockAgent(AgentInterface):
- raw_output: The agent's raw text output
"""
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
from src.ollama.provider import get_ollama_provider
from pydantic_ai.settings import ModelSettings
from pydantic_ai.messages import (
ModelRequest,
@@ -661,7 +661,7 @@ class TatlockAgent(AgentInterface):
ollama_model = OpenAIChatModel(
model_name=self.model_name,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider()
)
# Create agent with scoped tools
@@ -759,7 +759,7 @@ class TatlockAgent(AgentInterface):
str: Butler-toned response synthesized from all results
"""
from pydantic_ai.models.openai import OpenAIChatModel
from pydantic_ai.providers.ollama import OllamaProvider
from src.ollama.provider import get_ollama_provider
from pydantic_ai.messages import ModelRequest, ModelResponse, UserPromptPart, TextPart
logger.info(
@@ -802,7 +802,7 @@ class TatlockAgent(AgentInterface):
ollama_model = OpenAIChatModel(
model_name=self.model_name,
provider=OllamaProvider(base_url=base_url)
provider=get_ollama_provider()
)
# Synthesis agent uses butler prompt but no tools