Skip to content

Release: v1.1.8 - #523

Open
github-actions[bot] wants to merge 36 commits into
mainfrom
develop
Open

github-actions[bot] wants to merge 36 commits into
mainfrom
develop

Conversation

@github-actions

Copy link
Copy Markdown

Improvements

Technical

c-tonneslan and others added 5 commits May 27, 2026 21:37
Closes #494. The mobile nav 'Medical Suggester' link was pointing at
/login (which kicked users to the login page) and used the wrong word.
Point it at / (where the suggester lives, matching the desktop Header)
and rename to 'Medication Suggester' for consistency with everywhere
else in the app.

Signed-off-by: Charlie Tonneslan <cst0520@gmail.com>
- Update secret.template.yaml to show CNPG as the primary target
  with shared-cluster-rw.cloudnative-pg.svc.cluster.local
- Switch SQL_DATABASE default from balancer_dev to balancer
- Update dev.env.example to document CNPG as primary, RDS as legacy

Refs: #464
Related: CodeForPhilly/cfp-sandbox-cluster#162
- Move os/settings/HttpResponseNotFound imports to top in urls.py (E402)
- Add noqa: E402 to django.setup()-dependent imports in eval_assistant.py
- Remove unused INSTRUCTIONS import in eval_assistant.py (F401)

Refs: #464
docs: add local frontend and API notes (localhost:3000/8000)
@github-actions

github-actions Bot commented Jul 15, 2026 •

Copy link
Copy Markdown
Author

Changelog

- docs: add local frontend and API notes (localhost:3000/8000) [#515] @snaeem3
- Fix mobile nav 'Medication Suggester' link target and label [#512] @c-tonneslan
- feat: update DB config defaults from RDS to CloudNativePG [#514] @TineoC
- 388 Fix Suicide Risk Button Malfunction [#525] @snaeem3
- [#521] Research Agent Tools [#524] @sahilds1

sahilds1 and others added 24 commits July 15, 2026 18:49
Add ask_database as a second tool for the assistant, alongside the
existing semantic search_documents tool.

- Reuse the SELECT-only, ALLOWED_TABLES-guarded ask_database
  implementation from services/tools/database.py rather than
  reimplementing the query guards in the assistant.
- Add get_tools_schema() / make_tool_mapping() as an aggregation seam
  in tool_services.py so assistant_services.py no longer names
  individual tools; new tools are registered in one place.
- Build the ask_database schema in the flattened Responses-API shape
  (not the nested Chat Completions shape from services/tools), and
  defer the database_schema_string import to call time so importing
  the module never triggers a DB query.
- Split tool_services.py: move search_documents into search_tool.py and
  the agentic-loop helpers (handle_tool_calls_with_reasoning,
  invoke_functions_from_response) into agentic_loop.py, adding the
  imports each module needs.
- Point importers (assistant_services.py, test_tool_services.py) at the
  defining module for each symbol instead of re-exporting through
  tool_services.py.
- Document the search/SQL tool overlap risk and the import-time DB
  access caveat inline.
Replace the two parallel tool registries (get_tools_schema /
make_tool_mapping) with a single Tool dataclass and a TOOLS list, so each
tool's schema and callable live under one name and can't drift apart.

- Add Tool(name, description, parameters, run) with a .schema() method;
  define SEARCH_TOOL and ASK_DATABASE_TOOL instances and a single TOOLS
  list as the source of truth. Adding a tool is appending one Tool.
- Build the ask_database schema from Medication._meta (concrete_fields /
  db_table) instead of introspecting the live database, removing the
  import-time DB query and the deferred database_schema_string import.
- Bind the request user at dispatch time: invoke_functions_from_response
  and handle_tool_calls_with_reasoning now take (tools, user), index
  tools by name, and call tool.run(user=user, **arguments). This drops
  make_tool_mapping / make_search_tool_mapping and their closures.
- assistant_services builds the schema list with
  [tool.schema() for tool in TOOLS] and forwards TOOLS + user to the loop.
- Update tests to cover the Tool instances and pass (tools, user) to the
  loop; the tool_services.search_documents / ask_database patch paths
  still resolve.
Convert every relative import in api/views/assistant to its absolute
equivalent (assistant_services, search_tool, tool_services, urls, views).
Multi-dot forms like `...services.tools.database` and `..listMeds.models`
were error-prone to read and would break silently if a file's package
depth changed; the absolute paths are move-safe and unambiguous.

Expand the tool_services.py import comments to record why search_documents
and ask_database are imported as bare names: the tests patch them at their
use site (api.views.assistant.tool_services.<name>), not their definition
site, so mock.patch rebinds the reference SEARCH_TOOL.run actually resolves
at call time. Note the maintenance guard — qualifying those calls would
move the patch target and break the tests.

Move the _medication_schema_string helper to sit directly above its only
caller, ASK_DATABASE_TOOL, instead of above SEARCH_TOOL.

No behavior change: all bound names and patch targets are preserved.
Replace the _medication_schema_string() helper (which read columns from
Medication._meta) with a hand-written _MEDICATION_SCHEMA_STRING constant,
and drop the now-unused Medication import. The _meta approach auto-synced
with the model but dumped every column and pulled in an app-registry
dependency (AppRegistryNotReady if imported during app startup). For a
4-column, stable table, a curated constant is simpler, lets us hide
columns from the LLM (omit `id`, which it never filters on), and removes
the startup coupling — at the cost of a one-line manual update if the
table's columns ever change, which the comment calls out.

Note: this drops `id` from the schema the model sees (intentional
curation, not just a port of the old behavior).

Also document in the Tool docstring why behavior is a `run` field
(composition) rather than a subclass method: the tools differ only in
which function runs, so they are instances of one concept, not distinct
types. Add a TODO listing the signals that would justify flipping to
Tool(ABC) + per-tool subclasses (per-type state, overriding more than
run, or a per-type/abstractmethod-enforced contract).
…n-suggester

Fix mobile nav 'Medication Suggester' link target and label
The eval showed what the assistant said but not how it chose tools. Tool-call
info was produced in invoke_functions_from_response and dropped at every return
boundary; caught tool exceptions were fed back to the model as strings, so a run
where ask_database threw every question read as clean rows.

Carry it up as return values (over a mutable out-param: honest domain data that
grows via defaulted fields, no call-site churn):

- agentic_loop: add ToolCallStatus (OK/FAILED/UNREGISTERED — a bool was both
  redundant with error and lossy), ToolCall(name, status, arguments, output,
  error), and AssistantResult(output_text, response_id, tool_calls).
  invoke_functions_from_response returns (messages, tool_calls); the loop
  accumulates across iterations and returns AssistantResult.
- assistant_services: return AssistantResult (pass-through). Drops the TODO.
- views: read result fields; JSON body unchanged.
- eval_assistant: run_one times the call locally and adds tools_called,
  tool_call_count, tool_error_count, tool_calls_json, response_id, duration_s —
  making tool_error_count > 0 while error is None visible.

Deferred: token-cost, turn count, correctness scoring. Tests updated.
Hoist MODEL_NAME into assistant_services, import it in eval_assistant, and drop
the duplicated literal — the only functional change here.

The rest is comments. Token usage and turn count, the scoring layer, and the
INSTRUCTIONS sidecar were each designed in this pass and deliberately not built;
the TODOs sit where the work will happen and carry the reasoning.
…g-db-config

feat: update DB config defaults from RDS to CloudNativePG
388 Fix Suicide Risk Button Malfunction
The eval had never been run. Every test mocks run_assistant, so a green
suite proved run_one's row shaping but never that a CSV came out.

Three defects stopped it running at all: the sys.path depth, pandas
missing from the backend image, and the uv shebang and PEP 723 header,
which never described a runnable configuration.

A fourth is different in kind, and only the run could surface it. The
cold-start race on the embedding model does not stop the eval — it
corrupts it, completing normally and writing a CSV that looks clean.
Warming the model before the pool addresses it here.
Removed. None passes the mutation test — can a plausible one-line production
change turn it red *and* ship a bug?
  - test_ask_database_tool_run_ignores_user: single-arg forward; the wrong
    version raises TypeError on first call.
  - test_tools_registry_contains_both_tools: restated the TOOLS literal, so
    appending a tool was also how you broke the test.
  - test_run_assistant_sends_message_as_user_input: echoed a hardcoded dict.
  - test_run_assistant_forwards_tools_and_user_to_loop: `args[3] is TOOLS`,
    coupled to argument position.

Merged three groups into parametrize tables. Two gain coverage:
  - The loop test now checks the previous_response_id chain per turn, not just
    the first follow-up.
  - The FAILED/UNREGISTERED table exposes that `arguments` is parsed only
    inside the registered branch.
Tests whose assertions differ in kind stayed separate.

Added — flagged because they ride along with a commit that is otherwise
subtraction:
  - test_failed_status_... dispatched a MagicMock named "search_documents", so
    it duplicated the error table. It now dispatches the real SEARCH_TOOL, and
    is the only test proving the warm-up and search_tool.py fixes meet.
  - test_run_one_row_carries_every_csv_column: DictWriter raises on an extra
    key but fills a missing one with restval (""), so a column forgotten in one
    run_one row literal reaches the CSV as an empty cell, not an error.

Also reworded a tool_services.py comment asserting that tests patch
ask_database there — true until this commit.

Accepted: user -> run_assistant -> loop is no longer asserted. Bare positional
forward, no decision in it, both adjacent legs still covered.

22 tests -> 15 functions / 19 cases. Per-test rationale is in the docstrings
The new names say waht the functions do rather than borrowing the OpenAI Cookbook vocab
Errors in the run or values for  token usage and tool calls are to be None, not 0
"Turn" now means one whole run_assistant call and "iteration" means one
pass of the agentic loop (one responses.create).

Removed: model, response_id, tools_called, turn_count
Dropped tests that  cover telemetry, or failures that are loud on the first real call

Each case was checked by deliberately breaking the code using Claude Opus 5.5
ask_database's guards only check that a query starts with "select"
and contains "from api_medication", so a UNION or a second statement
can reach other tables, and the assistant endpoint is AllowAny

It also returns errors as text, so a failed query would be recorded as OK
[#521] Research Agent Tools: This PR gets the assistant's tool loop working, records every tool call and how many tokens it used, and adds an eval script so we can measure answers. Search failures now show up as FAILED instead of turning into confident answers. The endpoint's request and response are unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants