feat(examples): add web_search_chatbot example (#259)#287
Conversation
Adds a new examples/web_search_chatbot/ following the simple_chatbot layout, with a PydanticAI agent using duckduckgo_search_tool() for real web search and a keyless StubChatbot fallback. - DI selector keys off settings.llm_model_name alone, matching the other chatbot examples (no new WEB_SEARCH_ENABLED flag). - Adds the pydantic-ai-duckduckgo extra to pyproject.toml. - Unit tests cover stub flow, structural real-agent flow (offline via TestModel(call_tools=[])), and tool-invocation (offline via FunctionModel + a monkeypatched fake search tool). - Adds a copy-flow smoke ExampleCase so the example is verified to boot post-copy under make quickstart.
The web_search_chatbot unit tests instantiate PydanticAIChatbot directly (via TestModel/FunctionModel), which lazily imports duckduckgo_search_tool in __init__. CI's test job wasn't installing the pydantic-ai-duckduckgo extra, so those tests failed with ImportError in CI even though they passed locally.
…ision ChatMessageModel was using __tablename__ = 'chatbot_message', the same name as simple_chatbot's model, on the same SQLAlchemy Base.metadata. When the full test suite collects both example packages in one process, this raises InvalidRequestError. Renamed to 'web_search_chatbot_message', matching the unique-per-example convention used by chatbot_with_memory and chatbot_with_guardrails.
|
Investigated the remaining
I confirmed both fail identically on The one failure that was caused by this PR — a |
|
Hi @Mr-DooSun! Governor Footer link fixed, remaining CI failures are the pre-existing unrelated ones flagged above |
Mr-DooSun
left a comment
There was a problem hiding this comment.
Review — web_search_chatbot example (#259)
Verdict: Request changes — one blocking item (CI is red as a direct effect of this PR's ci.yml change). Everything else is small, and the implementation itself is solid.
Verified good — please keep as-is
- Container / selector / bootstrap / router wiring is an exact pattern match with
simple_chatbot(_chatbot_selectorkeyed onsettings.llm_model_name;wire(modules=[chatbot_router])with the imported module object). Do not change. - Copy-flow compliance verified on this head:
tools/check_examples_copyflow.py→ 0 violations across 246 files;test_example_survives_copy_to_src[web_search_chatbot]and the completeness meta-test pass. - Unit tests run 2 passed / 2 skipped in a no-pydantic-ai env; request validation (
prompt1–1000 chars,Path(ge=1)) is right; theweb_search_chatbot_messagetable name correctly avoids the sibling collision. - Security: the prompt-injection deferral (SEC-1 in the PR body) is accepted — search-tool output guardrails stay out of scope per #259, and the README production note covers it. Do not add guardrails in this PR.
Blocking — CI
See the inline comment on .github/workflows/ci.yml:66. Short version: the new extra installs pydantic_ai in CI for the first time, un-skipping six latent pre-existing test failures (tracked in #288 — please don't fix those here). Recommended path: revert the ci.yml line + add the duckduckgo importorskip guards (inline comment on the test file).
The failure is deterministic — re-running CI (e.g. the empty chore: trigger CI re-run commit at HEAD) cannot help. Please also drop that empty commit when you rebase: this repo merges with merge commits, so it would be preserved in main history permanently.
Summary-level findings (files outside the diff)
- [MEDIUM][REG/GOV] Only if the maintainer decides to keep the ci.yml extra:
make check-full(Makefile:132) and its "CI-parity" descriptions (AGENTS.md,.claude/rules/commands.md) must gain the same extra in the same PR — otherwise the documented local parity loop cannot reproduce CI, and the two new agent tests run in no documented local entrypoint. With the recommended revert, no action is needed. - [LOW][GOV]
docs/reference.md"Optional dependency extras" table: please add apydantic-ai-duckduckgorow (installspydantic-ai-slim[duckduckgo]→ddgs; enables this example's real search tool). The table's pre-existingopenai/otelomissions are handled separately — adding just your row is enough. - [DRIFT]
examples/README.md: add aweb_search_chatbot/row to the Available examples index table — every other example has one (suggested:|web_search_chatbot/| Agentic web-search tool use (DuckDuckGo, keyless) | LLM-calling | ✅ #287 |).
PR body
- The dangling
Fixes #line can be removed. - After the fixes, please re-verify the "Tests pass" checkbox and the Governor Footer
final-verdict: merge-readyagainst a green CI run — both are currently stale.
Thanks for the contribution — the DI/bootstrap/copy-flow work is exactly right, and the offline FunctionModel tool-invocation test is a nice pattern. Once the CI line is resolved and the README runbook actually boots the real path, this is a strong addition to the examples set.
4031451 to
96718f7
Compare
|
Hi @Mr-DooSun! Addressed all the review feedback:
All checks are passing now. Let me know if anything else needs adjustment! |
Mr-DooSun
left a comment
There was a problem hiding this comment.
Re-review — head 96718f7
Verdict: PASS on the code — posting as Comment (not Approve) because two summary-level items from the previous review are still unaddressed.
Verified fixed at this head — thank you, all confirmed
- duckduckgo
importorskipguards on both agent tests ✔ - README: valid UTF-8 ✔ · table name matches the model (
web_search_chatbot_message) ✔ · real-LLM runbook now installs the right extras and wipesquickstart.db✔ · DuckDuckGo typo ✔ · camelCase response keys ✔ - ci.yml extra install reverted → CI fully green at this head (all 6 jobs) ✔
- Empty re-run commit dropped ✔ · dangling
Fixes #removed ✔
All 7 resolved inline threads were re-verified against this head — every resolution is substantively justified.
Still open — carried over from the previous review's summary section
These items reference files outside this PR's diff, so no resolvable inline threads could anchor to them — which is likely why they slipped past the thread-resolution sweep. Please use this checklist as the tracker:
-
docs/reference.md— add the new extra to the "Optional dependency extras" table (after thepydantic-ai-googlerow, currently line 77):| `pydantic-ai-duckduckgo` | DuckDuckGo search tool for PydanticAI (`ddgs`) | `examples/web_search_chatbot/` real search tool | -
examples/README.md— add an index row to the Available examples table (LLM-calling group, after thechatbot_with_memory/row, currently line 116):| `web_search_chatbot/` | Agentic web-search tool use (DuckDuckGo, keyless) | LLM-calling | ✅ [#287](https://github.com/Mr-DooSun/fastapi-agent-blueprint/pull/287) | -
.github/workflows/ci.yml— restore the removed blank line so the file drops out of the diff entirely (see the inline comment; leftover from the revert in96718f7). - PR body — the intro ("Also fixes CI's
testjob…") and the Change Summary bullet ("Updated.github/workflows/ci.yml'stestjob to install thepydantic-ai-duckduckgoextra") still describe the change that96718f7reverted. Please update both — this repo merges with merge commits, so the PR body is preserved in history.
Please keep as-is
- The ci.yml revert direction is correct — do not re-add the extra to the test job (the latent failures it un-skips are tracked in #288).
- The
importorskipguards, DI/selector/bootstrap wiring, unique table name, and the offlineFunctionModeltool-invocation test are all right — do not change.
Once the four checkboxes land, I'll re-review and this becomes an Approve — the implementation itself is merge-quality.
- Add pydantic-ai-duckduckgo row to docs/reference.md's Optional dependency extras table. - Add web_search_chatbot/ row to examples/README.md's Available examples index table. - Restore the blank line in ci.yml left over from the earlier pydantic-ai-duckduckgo extra revert, so ci.yml drops out of the diff entirely.
|
Hi @Mr-DooSun! Addressed the remaining four items:
All checks are green. Let me know if anything else is needed! |
Adds a new examples/web_search_chatbot/ following the simple_chatbot layout, with a PydanticAI agent using duckduckgo_search_tool() for real web search and a keyless StubChatbot fallback.
Related Issue
Change Summary
examples/web_search_chatbot/example: PydanticAI Agent +duckduckgo_search_tool()for real web search, with a keylessStubChatbotfallback.WebSearchChatbotContainer/bootstrap_web_search_chatbot_domainadded following the auto-discovery naming convention; all other files keep the genericchatbot_*names.pydantic-ai-duckduckgoextra topyproject.toml.tests/unit/web_search_chatbot/) and a copy-flow smokeExampleCase(tests/integration/examples/test_copyflow_smoke.py).README.mdfor the example, following thechatbot_with_memoryREADME format.ci.ymlchange that installed thepydantic-ai-duckduckgoextra in thetestjob — it un-skipped two pre-existing latent test failures (tracked and fixed separately in tests: latent failures in envs with pydantic-ai installed (embedding openai guard + classification settings monkeypatch) #288).Type of Change
Checklist
ruff check src/)How to Test
Self-Structured Review Evidence
Reviewed against
docs/ai/shared/skills/review-pr.md§ Self-Structured Review Checklist anddocs/ai/shared/security-checklist.md.Coverage (OK/SKIP):
POST /v1/chathas no auth dependency — OK, matches the existing unauthenticatedsimple_chatbot/chatbot_with_memorypattern this repo already ships, not a new gap introduced here.simple_chatbot, no new credential paths).ExampleCaseadded for the new surface.pydantic-ai-duckduckgois a scoped optional extra.Findings (OPEN):
escape_for_prompt_xmlboundary-tagging +detect_prompt_injectionruntime guardrail pattern used by the RAG/classifier agents.infrastructure/chatbot/pydantic_ai_chatbot.pyregistersduckduckgo_search_tool()directly with no output-side escaping or guardrail call;docs/ai/shared/security-checklist.md§12 requires this for any dynamic content reaching the prompt.guardrails.pyand flagging that search results are untrusted content. The issue explicitly keeps the guardrailed RAG agent untouched and defers a shared_coreweb-search infra abstraction (which is where a reusable escaping/guardrail wrapper would land) to a future issue once there's a second consumer.Verdict: No OPEN BLOCKING or intent/contract-breaking HIGH — SEC-1 is closed via documented deferral, not left open.
Governor Footer
_coreweb-search abstraction (see issue examples: agentic web-search tool for PydanticAI agents (tool-calling follow-up to simple-chatbot) #259 "Out of scope").