Allow any provider/model supported by pydantic AI
This commit is contained in:
parent
4ac658e9dd
commit
d6d79d5e94
7 changed files with 63 additions and 42 deletions
|
|
@ -8,7 +8,7 @@ Retrieval-Augmented Generation (RAG) library on SQLite.
|
||||||
|
|
||||||
- **Local SQLite**: No external servers required
|
- **Local SQLite**: No external servers required
|
||||||
- **Multiple embedding providers**: Ollama, VoyageAI, OpenAI
|
- **Multiple embedding providers**: Ollama, VoyageAI, OpenAI
|
||||||
- **Multiple QA providers**: Ollama, OpenAI, Anthropic
|
- **Multiple QA providers**: Any provider/model supported by Pydantic AI
|
||||||
- **Hybrid search**: Vector + full-text search with Reciprocal Rank Fusion
|
- **Hybrid search**: Vector + full-text search with Reciprocal Rank Fusion
|
||||||
- **Reranking**: Default search result reranking with MixedBread AI or Cohere
|
- **Reranking**: Default search result reranking with MixedBread AI or Cohere
|
||||||
- **Question answering**: Built-in QA agents on your documents
|
- **Question answering**: Built-in QA agents on your documents
|
||||||
|
|
|
||||||
|
|
@ -55,13 +55,12 @@ OPENAI_API_KEY="your-api-key"
|
||||||
|
|
||||||
## Question Answering Providers
|
## Question Answering Providers
|
||||||
|
|
||||||
Configure which LLM provider to use for question answering.
|
Configure which LLM provider to use for question answering using the `provider:model` format. Any provider and model supported by [Pydantic AI](https://ai.pydantic.dev/models/) can be used.
|
||||||
|
|
||||||
### Ollama (Default)
|
### Ollama (Default)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
QA_PROVIDER="ollama"
|
QA_PROVIDER="ollama:qwen3"
|
||||||
QA_MODEL="qwen3"
|
|
||||||
OLLAMA_BASE_URL="http://localhost:11434"
|
OLLAMA_BASE_URL="http://localhost:11434"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -70,8 +69,7 @@ OLLAMA_BASE_URL="http://localhost:11434"
|
||||||
OpenAI QA is included in the default installation. Simply configure:
|
OpenAI QA is included in the default installation. Simply configure:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
QA_PROVIDER="openai"
|
QA_PROVIDER="openai:gpt-4o-mini" # or openai:gpt-4, openai:gpt-3.5-turbo, etc.
|
||||||
QA_MODEL="gpt-4o-mini" # or gpt-4, gpt-3.5-turbo, etc.
|
|
||||||
OPENAI_API_KEY="your-api-key"
|
OPENAI_API_KEY="your-api-key"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -80,11 +78,27 @@ OPENAI_API_KEY="your-api-key"
|
||||||
Anthropic QA is included in the default installation. Simply configure:
|
Anthropic QA is included in the default installation. Simply configure:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
QA_PROVIDER="anthropic"
|
QA_PROVIDER="anthropic:claude-3-5-haiku-20241022" # or anthropic:claude-3-5-sonnet-20241022, etc.
|
||||||
QA_MODEL="claude-3-5-haiku-20241022" # or claude-3-5-sonnet-20241022, etc.
|
|
||||||
ANTHROPIC_API_KEY="your-api-key"
|
ANTHROPIC_API_KEY="your-api-key"
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Other Providers
|
||||||
|
|
||||||
|
Any provider supported by Pydantic AI can be used. Examples include:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Google Gemini
|
||||||
|
QA_PROVIDER="gemini:gemini-1.5-flash"
|
||||||
|
|
||||||
|
# Groq
|
||||||
|
QA_PROVIDER="groq:llama-3.3-70b-versatile"
|
||||||
|
|
||||||
|
# Mistral
|
||||||
|
QA_PROVIDER="mistral:mistral-small-latest"
|
||||||
|
```
|
||||||
|
|
||||||
|
See the [Pydantic AI documentation](https://ai.pydantic.dev/models/) for the complete list of supported providers and models.
|
||||||
|
|
||||||
## Reranking
|
## Reranking
|
||||||
|
|
||||||
Reranking improves search quality by re-ordering the initial search results using specialized models. When enabled, the system retrieves more candidates (3x the requested limit) and then reranks them to return the most relevant results.
|
Reranking improves search quality by re-ordering the initial search results using specialized models. When enabled, the system retrieves more candidates (3x the requested limit) and then reranks them to return the most relevant results.
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,7 @@ class AppConfig(BaseModel):
|
||||||
RERANK_PROVIDER: str = "ollama"
|
RERANK_PROVIDER: str = "ollama"
|
||||||
RERANK_MODEL: str = "qwen3"
|
RERANK_MODEL: str = "qwen3"
|
||||||
|
|
||||||
QA_PROVIDER: str = "ollama"
|
QA_PROVIDER: str = "ollama:qwen3"
|
||||||
QA_MODEL: str = "qwen3"
|
|
||||||
|
|
||||||
CHUNK_SIZE: int = 256
|
CHUNK_SIZE: int = 256
|
||||||
CONTEXT_CHUNK_RADIUS: int = 0
|
CONTEXT_CHUNK_RADIUS: int = 0
|
||||||
|
|
|
||||||
|
|
@ -4,15 +4,10 @@ from haiku.rag.qa.agent import QuestionAnswerAgent
|
||||||
|
|
||||||
|
|
||||||
def get_qa_agent(client: HaikuRAG, use_citations: bool = False) -> QuestionAnswerAgent:
|
def get_qa_agent(client: HaikuRAG, use_citations: bool = False) -> QuestionAnswerAgent:
|
||||||
provider = Config.QA_PROVIDER
|
provider_model = Config.QA_PROVIDER
|
||||||
model_name = Config.QA_MODEL
|
|
||||||
|
|
||||||
if provider in ("openai", "anthropic", "ollama"):
|
|
||||||
return QuestionAnswerAgent(
|
return QuestionAnswerAgent(
|
||||||
client=client,
|
client=client,
|
||||||
provider=provider,
|
provider_model=provider_model,
|
||||||
model=model_name,
|
|
||||||
use_citations=use_citations,
|
use_citations=use_citations,
|
||||||
)
|
)
|
||||||
|
|
||||||
raise ValueError(f"Unsupported QA provider: {provider}")
|
|
||||||
|
|
|
||||||
|
|
@ -23,15 +23,14 @@ class QuestionAnswerAgent:
|
||||||
def __init__(
|
def __init__(
|
||||||
self,
|
self,
|
||||||
client: HaikuRAG,
|
client: HaikuRAG,
|
||||||
provider: str,
|
provider_model: str,
|
||||||
model: str,
|
|
||||||
use_citations: bool = False,
|
use_citations: bool = False,
|
||||||
q: float = 0.0,
|
q: float = 0.0,
|
||||||
):
|
):
|
||||||
self._client = client
|
self._client = client
|
||||||
|
|
||||||
system_prompt = SYSTEM_PROMPT_WITH_CITATIONS if use_citations else SYSTEM_PROMPT
|
system_prompt = SYSTEM_PROMPT_WITH_CITATIONS if use_citations else SYSTEM_PROMPT
|
||||||
model_obj = self._get_model(provider, model)
|
model_obj = self._get_model(provider_model)
|
||||||
|
|
||||||
self._agent = Agent(
|
self._agent = Agent(
|
||||||
model=model_obj,
|
model=model_obj,
|
||||||
|
|
@ -58,19 +57,21 @@ class QuestionAnswerAgent:
|
||||||
for chunk, score in expanded_results
|
for chunk, score in expanded_results
|
||||||
]
|
]
|
||||||
|
|
||||||
def _get_model(self, provider: str, model: str):
|
def _get_model(self, provider_model: str):
|
||||||
"""Get the appropriate model object for the provider."""
|
"""Get the appropriate model object for the provider:model format."""
|
||||||
if provider == "openai":
|
if ":" not in provider_model:
|
||||||
return f"openai:{model}"
|
raise ValueError(f"Invalid provider:model format: {provider_model}")
|
||||||
elif provider == "anthropic":
|
|
||||||
return f"anthropic:{model}"
|
provider, model = provider_model.split(":", 1)
|
||||||
elif provider == "ollama":
|
|
||||||
|
if provider == "ollama":
|
||||||
return OpenAIModel(
|
return OpenAIModel(
|
||||||
model_name=model,
|
model_name=model,
|
||||||
provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
|
provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
raise ValueError(f"Unsupported provider: {provider}")
|
# For other providers, use the provider:model string directly
|
||||||
|
return provider_model
|
||||||
|
|
||||||
async def answer(self, question: str) -> str:
|
async def answer(self, question: str) -> str:
|
||||||
"""Answer a question using the RAG system."""
|
"""Answer a question using the RAG system."""
|
||||||
|
|
|
||||||
|
|
@ -37,14 +37,24 @@ class LLMJudgeResponseSchema(BaseModel):
|
||||||
class LLMJudge:
|
class LLMJudge:
|
||||||
"""LLM-as-judge for evaluating answer equivalence using Pydantic AI."""
|
"""LLM-as-judge for evaluating answer equivalence using Pydantic AI."""
|
||||||
|
|
||||||
def __init__(self, model: str = Config.QA_MODEL):
|
def __init__(self, provider_model: str = Config.QA_PROVIDER):
|
||||||
self.model = model
|
self.provider_model = provider_model
|
||||||
|
|
||||||
|
# Parse provider:model format
|
||||||
|
if ":" not in provider_model:
|
||||||
|
raise ValueError(f"Invalid provider:model format: {provider_model}")
|
||||||
|
|
||||||
|
provider, model = provider_model.split(":", 1)
|
||||||
|
|
||||||
|
if provider == "ollama":
|
||||||
# Create Ollama model
|
# Create Ollama model
|
||||||
ollama_model = OpenAIModel(
|
ollama_model = OpenAIModel(
|
||||||
model_name=model,
|
model_name=model,
|
||||||
provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
|
provider=OllamaProvider(base_url=f"{Config.OLLAMA_BASE_URL}/v1"),
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# For other providers, use the provider:model string directly
|
||||||
|
ollama_model = provider_model
|
||||||
|
|
||||||
# Create Pydantic AI agent
|
# Create Pydantic AI agent
|
||||||
self._agent = Agent(
|
self._agent = Agent(
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ ANTHROPIC_AVAILABLE = bool(Config.ANTHROPIC_API_KEY)
|
||||||
async def test_qa_ollama(qa_corpus: Dataset):
|
async def test_qa_ollama(qa_corpus: Dataset):
|
||||||
"""Test Ollama QA with LLM judge."""
|
"""Test Ollama QA with LLM judge."""
|
||||||
client = HaikuRAG(":memory:")
|
client = HaikuRAG(":memory:")
|
||||||
qa = QuestionAnswerAgent(client, "ollama", "qwen3")
|
qa = QuestionAnswerAgent(client, provider_model="ollama:qwen3")
|
||||||
llm_judge = LLMJudge()
|
llm_judge = LLMJudge()
|
||||||
|
|
||||||
doc = qa_corpus[1]
|
doc = qa_corpus[1]
|
||||||
|
|
@ -39,7 +39,7 @@ async def test_qa_ollama(qa_corpus: Dataset):
|
||||||
async def test_qa_openai(qa_corpus: Dataset):
|
async def test_qa_openai(qa_corpus: Dataset):
|
||||||
"""Test OpenAI QA with LLM judge."""
|
"""Test OpenAI QA with LLM judge."""
|
||||||
client = HaikuRAG(":memory:")
|
client = HaikuRAG(":memory:")
|
||||||
qa = QuestionAnswerAgent(client, "openai", "gpt-4o-mini")
|
qa = QuestionAnswerAgent(client, provider_model="openai:gpt-4o-mini")
|
||||||
llm_judge = LLMJudge()
|
llm_judge = LLMJudge()
|
||||||
|
|
||||||
doc = qa_corpus[1]
|
doc = qa_corpus[1]
|
||||||
|
|
@ -63,7 +63,9 @@ async def test_qa_openai(qa_corpus: Dataset):
|
||||||
async def test_qa_anthropic(qa_corpus: Dataset):
|
async def test_qa_anthropic(qa_corpus: Dataset):
|
||||||
"""Test Anthropic QA with LLM judge."""
|
"""Test Anthropic QA with LLM judge."""
|
||||||
client = HaikuRAG(":memory:")
|
client = HaikuRAG(":memory:")
|
||||||
qa = QuestionAnswerAgent(client, "anthropic", "claude-3-5-haiku-20241022")
|
qa = QuestionAnswerAgent(
|
||||||
|
client, provider_model="anthropic:claude-3-5-haiku-20241022"
|
||||||
|
)
|
||||||
llm_judge = LLMJudge()
|
llm_judge = LLMJudge()
|
||||||
|
|
||||||
doc = qa_corpus[1]
|
doc = qa_corpus[1]
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue