Squash-merge of fix/ci-pipeline-corrections. Drives the full test suite
to 69/69 green on the ITSulu K8s cluster and fixes two production bugs.
Production fixes:
- Email template migrated from dead Odoo Mako (${}/% for) to Odoo 17
inline_template ({{ }}) + qweb body (type="html", t-out/t-foreach/t-if).
Notification emails previously rendered raw code in the subject/body.
- _create_blog_post now writes 'content': llm_response.body_html — every
auto-generated post was publishing empty.
- Removed duplicate itsulu_social_id field (startup warning).
Testing & infra:
- CI pipeline corrected (stage order, DB auth, junit artifact, addons path).
- E2E moved to ephemeral jobs in the itsulu-testing K8s namespace.
- Test code brought up to Odoo 17 (mail rendering, blog.post.content,
pytest-bdd env fixture, _render_field).
Versioning:
- Introduce MAJOR.MINOR.PATCH scheme, VERSION file, scripts/bump-version.sh,
CHANGELOG.md; first release v0.4.8. CLAUDE.md §15 documents the process.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
46 lines
2 KiB
Gherkin
46 lines
2 KiB
Gherkin
# =================================================================
|
|
# Multi-provider LLM routing
|
|
# =================================================================
|
|
Feature: Multi-provider LLM routing
|
|
As an ITSulu admin
|
|
I want to choose which LLM provider generates each blog post
|
|
So that I can control cost, quality, and availability
|
|
|
|
Background:
|
|
Given the Anthropic API key is configured in Settings
|
|
And the blog "ITSulu Insights" exists in Odoo
|
|
|
|
Scenario: Anthropic provider generates blog content
|
|
Given provider is "anthropic" and model is "claude-sonnet-4-20250514"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the Anthropic /v1/messages endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: OpenAI provider generates blog content
|
|
Given provider is "openai" and model is "gpt-4o"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the OpenAI /v1/chat/completions endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Gemini provider generates blog content
|
|
Given provider is "gemini" and model is "gemini-2.0-flash"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls the Google Gemini API endpoint
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Ollama provider generates blog content using local model
|
|
Given provider is "ollama" and model is "mistral"
|
|
And the Ollama base URL is "http://localhost:11434"
|
|
When the LLM router is called with a prompt
|
|
Then the router calls http://localhost:11434/api/chat
|
|
And returns a non-empty string response
|
|
|
|
Scenario: Unknown provider raises configuration error
|
|
Given provider is "unknown_provider" and model is "some-model"
|
|
When the LLM router is called with a prompt
|
|
Then a UserError is raised with message containing "not configured"
|
|
|
|
Scenario: Token usage is recorded in generation log
|
|
Given provider is "anthropic" and model is "claude-sonnet-4-20250514"
|
|
When the LLM router is called with a prompt
|
|
Then the generation log record contains tokens_used > 0
|