feat: User can submit a query and receive a response#13
Conversation
📝 WalkthroughWalkthroughThis PR replaces direct Whoosh index search with an agent-based question-answering flow. It adds a ChangesAgent Query Answering Feature
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant CLI as CLI (__main__.py)
participant UseCase as find_answer
participant Agent as research agent
participant Tool as search tool
participant Index as DocumentIndex
User->>CLI: run --query "..."
CLI->>CLI: validate query & index
CLI->>Tool: make_search_tool(index)
CLI->>Agent: make_openai_research_agent(prompt)
CLI->>UseCase: find_answer(query, agent, [tool])
UseCase->>Agent: research_query(query, tools)
Agent->>Tool: search(phrase)
Tool->>Index: index.search(query)
Index-->>Tool: results
Tool-->>Agent: stringified results
Agent-->>UseCase: QueryResponse
UseCase-->>CLI: QueryResponse
CLI-->>User: print answer + citations
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
🧹 Nitpick comments (5)
todo.md (3)
50-80: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMark US-005 as done.
All three scenarios here are checked off, and the new agent-based answer flow is part of this PR. Keeping the story in
in progressno longer matches the implementation.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@todo.md` around lines 50 - 80, The US-005 story is still marked as in progress even though all scenarios are complete and the agent-based answer flow is implemented. Update the status in todo.md by changing the US-005 checkbox from in progress to done, keeping the existing scenario checklist intact. Use the US-005 section header and its current status marker to locate the entry.
25-35: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMove US-007 out of
todo.The CLI query flow and structured response are already implemented in this PR, so leaving this story open makes the tracker stale. Move it to
doneunless there are still open acceptance criteria.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@todo.md` around lines 25 - 35, Move the US-007 story out of todo.md into the done tracker because the CLI query flow is already implemented. Update the checklist entry for US-007 and its scenarios so the status reflects completion, and verify there are no remaining open acceptance criteria tied to the CLI query response or error handling before marking it done.
124-135: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winMake the parked status explicit.
US-001is marked complete even though both scenarios are still unchecked. That reads as “done” and “not done” at the same time; either leave it open or move it to a dedicated parked state without a checked parent item.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@todo.md` around lines 124 - 135, The parked item status is inconsistent because US-001 is marked complete while its scenarios remain unchecked. Update the TODO entry around US-001 so the parent item is not checked, or move it into a clearly labeled parked state without a completed checkbox, and keep the scenario checkboxes as-is to reflect the true status.src/docs_buddy/adapters/__init__.py (1)
19-19: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAvoid wildcard import flagged by Ruff (F403).
from .agent import *re-exports everything defined/imported inagent.py(includingos,json,asyncio,openai,agents), and Ruff can't verify what's actually used downstream.♻️ Proposed fix
-from .agent import * +from .agent import ( + AgentError, + ToolError, + make_search_tool, + make_fake_research_agent, + make_openai_research_agent, + run_agent, +)🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@src/docs_buddy/adapters/__init__.py` at line 19, Remove the wildcard re-export in __init__.py that imports everything from the agent module, since Ruff flags it and it leaks unrelated symbols. Update the package export surface in src/docs_buddy/adapters/__init__.py by replacing from .agent import * with explicit imports or a controlled __all__ based on the public API in agent.py, so only the intended adapter symbols are exposed.Source: Linters/SAST tools
notebooks/docs_searching_agent.org (1)
69-93: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueNotebook's
QueryResponse/field naming diverges from the finalized domain type.The local
QueryResponsehere usesfinal_answer, while the shippeddomain.QueryResponseusesanswer(seesrc/docs_buddy/domain/__init__.py). Since this notebook demonstrates the same flow now implemented inadapters/agent.py, aligning field names would help readers map the demo to the real API.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@notebooks/docs_searching_agent.org` around lines 69 - 93, Align the notebook’s response shape with the real domain model by renaming the local QueryResponse field final_answer to answer and updating generate_final_answer to construct it with the same field name; keep the dataclass, __str__, and function_tool example in sync with the shipped domain.QueryResponse and the adapter flow so readers see the same API surface as in adapters/agent.py.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@docs/design.md`:
- Line 5: The phrasing in the design description should use the standard
hyphenated term “domain-driven design” instead of “domain driven design.” Update
the wording in the affected prose so the description remains the same but
follows the correct compound modifier convention.
In `@src/docs_buddy/adapters/agent.py`:
- Around line 118-158: `openai_agent` only catches `json.JSONDecodeError`, so
malformed or unexpected agent output and `run_agent` failures can escape as
non-DocsBuddyError exceptions. Widen the exception handling around both
`asyncio.run(run_agent(...))` and `domain.QueryResponse.fromstring(output)` to
translate any parse/type/network/runtime failure into `AgentError`, keeping the
fallback path in `find_answer` intact. Use the existing `openai_agent` and
`AgentError` symbols to locate the change.
- Around line 72-93: The generate_final_response tool currently returns a
QueryResponse object, but openai_agent expects a JSON/string payload and later
passes result.final_output into QueryResponse.fromstring(...). Update
generate_final_response to return the string form of domain.QueryResponse (or
adjust the downstream parsing to consume the object directly) while keeping the
existing error handling in place. Use the generate_final_response function and
domain.QueryResponse as the key points to locate the fix.
In `@src/docs_buddy/entrypoints/cli/__main__.py`:
- Around line 91-108: The CLI flow in the main entrypoint creates the research
agent without handling `AgentError`, so missing OpenAI configuration currently
crashes with an unhandled traceback. Wrap the
`adapters.make_openai_research_agent(system_prompt)` call in the same style as
the nearby `domain.InvalidQueryError` and missing-index handling, catch
`AgentError`, log a clear CLI error, and exit nonzero; keep the fix localized to
the entrypoint logic that builds `research_user_query` before calling
`services.find_answer`.
---
Nitpick comments:
In `@notebooks/docs_searching_agent.org`:
- Around line 69-93: Align the notebook’s response shape with the real domain
model by renaming the local QueryResponse field final_answer to answer and
updating generate_final_answer to construct it with the same field name; keep
the dataclass, __str__, and function_tool example in sync with the shipped
domain.QueryResponse and the adapter flow so readers see the same API surface as
in adapters/agent.py.
In `@src/docs_buddy/adapters/__init__.py`:
- Line 19: Remove the wildcard re-export in __init__.py that imports everything
from the agent module, since Ruff flags it and it leaks unrelated symbols.
Update the package export surface in src/docs_buddy/adapters/__init__.py by
replacing from .agent import * with explicit imports or a controlled __all__
based on the public API in agent.py, so only the intended adapter symbols are
exposed.
In `@todo.md`:
- Around line 50-80: The US-005 story is still marked as in progress even though
all scenarios are complete and the agent-based answer flow is implemented.
Update the status in todo.md by changing the US-005 checkbox from in progress to
done, keeping the existing scenario checklist intact. Use the US-005 section
header and its current status marker to locate the entry.
- Around line 25-35: Move the US-007 story out of todo.md into the done tracker
because the CLI query flow is already implemented. Update the checklist entry
for US-007 and its scenarios so the status reflects completion, and verify there
are no remaining open acceptance criteria tied to the CLI query response or
error handling before marking it done.
- Around line 124-135: The parked item status is inconsistent because US-001 is
marked complete while its scenarios remain unchecked. Update the TODO entry
around US-001 so the parent item is not checked, or move it into a clearly
labeled parked state without a completed checkbox, and keep the scenario
checkboxes as-is to reflect the true status.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: b27c1f0a-5231-43de-8d35-6f97768483ff
📒 Files selected for processing (14)
Makefiledocs/design.mdnotebooks/docs_searching_agent.orgnotebooks/openai_agent_sdk_intro.orgpyproject.tomlsrc/docs_buddy/adapters/__init__.pysrc/docs_buddy/adapters/agent.pysrc/docs_buddy/domain/__init__.pysrc/docs_buddy/entrypoints/cli/__main__.pysrc/docs_buddy/services/use_cases.pytests/integration/test_adapters.pytests/unit/test_services.pytests/unit/test_tools.pytodo.md
|
|
||
| ## Repository Structure | ||
|
|
||
| It matches the domain driven design architecture of the application |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Hyphenate "domain-driven design."
✏️ Proposed fix
-It matches the domain driven design architecture of the application
+It matches the domain-driven design architecture of the application📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| It matches the domain driven design architecture of the application | |
| It matches the domain-driven design architecture of the application |
🧰 Tools
🪛 LanguageTool
[grammar] ~5-~5: Use a hyphen to join words.
Context: ...ository Structure It matches the domain driven design architecture of the applic...
(QB_NEW_EN_HYPHEN)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/design.md` at line 5, The phrasing in the design description should use
the standard hyphenated term “domain-driven design” instead of “domain driven
design.” Update the wording in the affected prose so the description remains the
same but follows the correct compound modifier convention.
Source: Linters/SAST tools
| @agents.function_tool | ||
| def generate_final_response( | ||
| final_answer: str, citations: list[str] | ||
| ) -> domain.QueryResponse: | ||
| """Generates the final response to the user's query in a structed format | ||
|
|
||
| Args: | ||
| final_answer (str): The final answer to the user's query | ||
| citations (list[str]): A list of paths from the search tool results that were instrumental | ||
| in generating the answer | ||
|
|
||
| Returns: | ||
| QueryResponse: A structured response to the user's query | ||
| """ | ||
|
|
||
| try: | ||
| final_response = domain.QueryResponse(final_answer, citations) | ||
| except (TypeError, ValueError) as exc: | ||
| raise AgentError("Could not create response") from exc | ||
|
|
||
| return final_response | ||
|
|
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🌐 Web query:
Does openai-agents-python version 0.17.7 stringify a dataclass return value from a StopAtTools tool via str() before setting final_output?
💡 Result:
In openai-agents-python version 0.17.7, StopAtTools does not automatically stringify a dataclass return value via str before setting it as the final output. When StopAtTools is configured to stop on a specific tool, the tool's return value is used directly as the final output [1][2]. If a tool returns a dataclass, that dataclass object is passed through as the final output, preserving its type [3]. Users are then able to access this output in a structured format, for example, by using result.final_output_as(MyDataclassType) after running the agent [3]. There is no logic within the StopAtTools handling mechanism that coerces these return values to strings [1][2].
Citations:
- 1: https://github.com/openai/openai-agents-python/blob/main/src/agents/agent.py
- 2: https://github.com/openai/openai-agents-python/blob/f9763495b86afcf0c421451a92200e1141fa8dcb/src/agents/agent.py
- 3: Chain-of-thought and structured output openai/openai-agents-python#800
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the relevant adapter and response types, then print the surrounding lines.
git ls-files | rg '(^|/)src/docs_buddy/adapters/agent\.py$|(^|/)src/docs_buddy/.+\.py$|(^|/)pyproject\.toml$'
printf '\n--- agent.py ---\n'
cat -n src/docs_buddy/adapters/agent.py | sed -n '1,240p'
printf '\n--- query response references ---\n'
rg -n "class QueryResponse|fromstring\(|final_output|StopAtTools|generate_final_response|openai_agent" src/docs_buddy -nRepository: programmer-ke/docs-buddy
Length of output: 8509
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the QueryResponse implementation and agent output handling.
cat -n src/docs_buddy/domain.py | sed -n '1,240p'
printf '\n--- openai_agent references ---\n'
rg -n "fromstring\(|QueryResponse\(|final_output|StopAtTools|result\.final_output" src/docs_buddy -nRepository: programmer-ke/docs-buddy
Length of output: 219
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf '\n--- pyproject version pin ---\n'
rg -n 'openai-agents|agents==' pyproject.toml
printf '\n--- domain/__init__.py around QueryResponse ---\n'
cat -n src/docs_buddy/domain/__init__.py | sed -n '80,140p'Repository: programmer-ke/docs-buddy
Length of output: 2034
Return a JSON string from generate_final_response
StopAtTools passes the tool return value through as result.final_output, so openai_agent is handing a QueryResponse object to QueryResponse.fromstring(...). Return str(domain.QueryResponse(...)) here, or parse result.final_output as a QueryResponse directly.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/docs_buddy/adapters/agent.py` around lines 72 - 93, The
generate_final_response tool currently returns a QueryResponse object, but
openai_agent expects a JSON/string payload and later passes result.final_output
into QueryResponse.fromstring(...). Update generate_final_response to return the
string form of domain.QueryResponse (or adjust the downstream parsing to consume
the object directly) while keeping the existing error handling in place. Use the
generate_final_response function and domain.QueryResponse as the key points to
locate the fix.
| def openai_agent( | ||
| query: domain.Query, tools: list[Callable] | ||
| ) -> domain.QueryResponse: | ||
| """OpenaAI compatible agent that answers user's query""" | ||
|
|
||
| final_response_callout = ( | ||
| "IMPORTANT: Always call generate_final_response as your final output." | ||
| ) | ||
|
|
||
| system_instructions = f"""\ | ||
| {prompt} | ||
|
|
||
| {final_response_callout} | ||
| """ | ||
|
|
||
| agent = agents.Agent( | ||
| name="Docs Buddy Agent", | ||
| instructions=textwrap.dedent(system_instructions), | ||
| tools=[agents.function_tool(func) for func in tools] # type: ignore[arg-type] | ||
| + [generate_final_response], | ||
| tool_use_behavior=agents.agent.StopAtTools( | ||
| stop_at_tool_names=["generate_final_response"] | ||
| ), | ||
| ) | ||
|
|
||
| result = asyncio.run( | ||
| run_agent( | ||
| agent, | ||
| str(query), | ||
| agents.RunConfig(model_provider=CUSTOM_MODEL_PROVIDER), | ||
| ) | ||
| ) | ||
|
|
||
| output: str = result.final_output | ||
| try: | ||
| response = domain.QueryResponse.fromstring(output) | ||
| except json.JSONDecodeError as exc: | ||
| raise AgentError("Could not parse query response from llm") from exc | ||
| return response | ||
|
|
||
| return openai_agent |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
Incomplete exception handling lets malformed agent output crash the caller instead of falling back gracefully.
except json.JSONDecodeError only covers decode failures. If output is valid JSON but doesn't exactly match QueryResponse's fields (extra/missing keys), domain.QueryResponse.fromstring raises TypeError, which is not a DocsBuddyError and will propagate straight through openai_agent, then through find_answer's except DocsBuddyError handler (services/use_cases.py lines 186-200) unhandled — defeating the intended "Something went wrong, please try again" fallback and crashing the CLI/caller instead.
The same applies to any other unexpected exception from asyncio.run(run_agent(...)) (e.g. network/timeout/API errors from the openai client), none of which are DocsBuddyError subtypes.
🛡️ Proposed fix to widen error translation
output: str = result.final_output
try:
response = domain.QueryResponse.fromstring(output)
- except json.JSONDecodeError as exc:
+ except (json.JSONDecodeError, TypeError) as exc:
raise AgentError("Could not parse query response from llm") from exc
return responseConsider also wrapping the asyncio.run(run_agent(...)) call so unexpected client/network errors are translated into AgentError before reaching find_answer.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| def openai_agent( | |
| query: domain.Query, tools: list[Callable] | |
| ) -> domain.QueryResponse: | |
| """OpenaAI compatible agent that answers user's query""" | |
| final_response_callout = ( | |
| "IMPORTANT: Always call generate_final_response as your final output." | |
| ) | |
| system_instructions = f"""\ | |
| {prompt} | |
| {final_response_callout} | |
| """ | |
| agent = agents.Agent( | |
| name="Docs Buddy Agent", | |
| instructions=textwrap.dedent(system_instructions), | |
| tools=[agents.function_tool(func) for func in tools] # type: ignore[arg-type] | |
| + [generate_final_response], | |
| tool_use_behavior=agents.agent.StopAtTools( | |
| stop_at_tool_names=["generate_final_response"] | |
| ), | |
| ) | |
| result = asyncio.run( | |
| run_agent( | |
| agent, | |
| str(query), | |
| agents.RunConfig(model_provider=CUSTOM_MODEL_PROVIDER), | |
| ) | |
| ) | |
| output: str = result.final_output | |
| try: | |
| response = domain.QueryResponse.fromstring(output) | |
| except json.JSONDecodeError as exc: | |
| raise AgentError("Could not parse query response from llm") from exc | |
| return response | |
| return openai_agent | |
| def openai_agent( | |
| query: domain.Query, tools: list[Callable] | |
| ) -> domain.QueryResponse: | |
| """OpenaAI compatible agent that answers user's query""" | |
| final_response_callout = ( | |
| "IMPORTANT: Always call generate_final_response as your final output." | |
| ) | |
| system_instructions = f"""\ | |
| {prompt} | |
| {final_response_callout} | |
| """ | |
| agent = agents.Agent( | |
| name="Docs Buddy Agent", | |
| instructions=textwrap.dedent(system_instructions), | |
| tools=[agents.function_tool(func) for func in tools] # type: ignore[arg-type] | |
| [generate_final_response], | |
| tool_use_behavior=agents.agent.StopAtTools( | |
| stop_at_tool_names=["generate_final_response"] | |
| ), | |
| ) | |
| result = asyncio.run( | |
| run_agent( | |
| agent, | |
| str(query), | |
| agents.RunConfig(model_provider=CUSTOM_MODEL_PROVIDER), | |
| ) | |
| ) | |
| output: str = result.final_output | |
| try: | |
| response = domain.QueryResponse.fromstring(output) | |
| except (json.JSONDecodeError, TypeError) as exc: | |
| raise AgentError("Could not parse query response from llm") from exc | |
| return response | |
| return openai_agent |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/docs_buddy/adapters/agent.py` around lines 118 - 158, `openai_agent` only
catches `json.JSONDecodeError`, so malformed or unexpected agent output and
`run_agent` failures can escape as non-DocsBuddyError exceptions. Widen the
exception handling around both `asyncio.run(run_agent(...))` and
`domain.QueryResponse.fromstring(output)` to translate any
parse/type/network/runtime failure into `AgentError`, keeping the fallback path
in `find_answer` intact. Use the existing `openai_agent` and `AgentError`
symbols to locate the change.
| try: | ||
| query = domain.Query(args.query) | ||
| except domain.InvalidQueryError as exc: | ||
| log.error("Invalid query detected: %s", exc) | ||
| sys.exit(1) | ||
|
|
||
| base_url = f"https://github.com/{repo_id}/blob/main/" | ||
|
|
||
| tools = [adapters.make_search_tool(document_index)] | ||
|
|
||
| system_prompt = textwrap.dedent("""\ | ||
| You are a documentation assistant equipped to answer user queries | ||
| by searching the docs. | ||
| """) | ||
|
|
||
| research_user_query = adapters.make_openai_research_agent(system_prompt) | ||
|
|
||
| response = services.find_answer(query, research_user_query, tools) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Unhandled AgentError when OpenAI credentials aren't configured.
adapters.make_openai_research_agent(system_prompt) raises AgentError if OPENAI_BASE_URL/OPENAI_API_KEY/DOCS_BUDDY_MODEL_NAME are missing. Unlike the invalid-query and missing-index cases just above it in this same function, this call isn't wrapped in a try/except, so it will surface as an unhandled traceback rather than a clean CLI error.
🛡️ Proposed fix
- research_user_query = adapters.make_openai_research_agent(system_prompt)
+ try:
+ research_user_query = adapters.make_openai_research_agent(system_prompt)
+ except adapters.AgentError as exc:
+ log.error("Agent configuration error: %s", exc)
+ sys.exit(1)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| try: | |
| query = domain.Query(args.query) | |
| except domain.InvalidQueryError as exc: | |
| log.error("Invalid query detected: %s", exc) | |
| sys.exit(1) | |
| base_url = f"https://github.com/{repo_id}/blob/main/" | |
| tools = [adapters.make_search_tool(document_index)] | |
| system_prompt = textwrap.dedent("""\ | |
| You are a documentation assistant equipped to answer user queries | |
| by searching the docs. | |
| """) | |
| research_user_query = adapters.make_openai_research_agent(system_prompt) | |
| response = services.find_answer(query, research_user_query, tools) | |
| try: | |
| query = domain.Query(args.query) | |
| except domain.InvalidQueryError as exc: | |
| log.error("Invalid query detected: %s", exc) | |
| sys.exit(1) | |
| base_url = f"https://github.com/{repo_id}/blob/main/" | |
| tools = [adapters.make_search_tool(document_index)] | |
| system_prompt = textwrap.dedent("""\ | |
| You are a documentation assistant equipped to answer user queries | |
| by searching the docs. | |
| """) | |
| try: | |
| research_user_query = adapters.make_openai_research_agent(system_prompt) | |
| except adapters.AgentError as exc: | |
| log.error("Agent configuration error: %s", exc) | |
| sys.exit(1) | |
| response = services.find_answer(query, research_user_query, tools) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/docs_buddy/entrypoints/cli/__main__.py` around lines 91 - 108, The CLI
flow in the main entrypoint creates the research agent without handling
`AgentError`, so missing OpenAI configuration currently crashes with an
unhandled traceback. Wrap the
`adapters.make_openai_research_agent(system_prompt)` call in the same style as
the nearby `domain.InvalidQueryError` and missing-index handling, catch
`AgentError`, log a clear CLI error, and exit nonzero; keep the fix localized to
the entrypoint logic that builds `research_user_query` before calling
`services.find_answer`.
This enable the user get an answer from the agent curated with results from the document index.
Added a
find_answeruse case that assembles the system prompt, user query and search tool and uses themto get a response from the agent.
Summary by CodeRabbit
New Features
Bug Fixes
Documentation