Skip to content

feat(dev-api): add /v1/dev/user/ask and the "omi ask" CLI command#10314

Open
aryanorastar wants to merge 1 commit into
BasedHardware:mainfrom
aryanorastar:feat/cli-omi-ask
Open

feat(dev-api): add /v1/dev/user/ask and the "omi ask" CLI command#10314
aryanorastar wants to merge 1 commit into
BasedHardware:mainfrom
aryanorastar:feat/cli-omi-ask

Conversation

@aryanorastar

@aryanorastar aryanorastar commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

What

omi ask "<question>" — ask a natural-language question and get an answer grounded in your own Omi conversations, from the terminal or any Developer-API caller. No desktop app required.

omi ask "what did I decide about pricing last week?"
omi ask "what did I promise Sarah in yesterday's standup?"
omi --json ask "open questions from my last 3 meetings" --limit 3

The context that lives in your conversations, made queryable where developers actually work — the terminal, scripts, CI — not just the app.

How

  • BackendPOST /v1/dev/user/ask (conversations:read scope). Semantically searches the caller's conversations (search_conversations), then synthesizes a cited answer from the most relevant ones (qa_rag) — the same retrieval + RAG the chat surface already uses. Read-only; locked conversations are excluded by the search layer; no LLM call (no cost) when nothing relevant is found.
  • CLI — a thin top-level omi ask command (--limit, --timezone, --json) that renders the answer plus its conversation sources.

Deliberately small: it reuses the existing search + RAG rather than adding new machinery, and grounds on conversations (staying out of the memory-compat rollout path).

Rate limiting / cost guard

Addressing @Git-on-my-level's review — the endpoint invokes an LLM per call, so it now carries a dedicated per-key budget instead of riding the cheap list-read limit:

  • New dev:ask policy — 25 / hour, per key — in utils/rate_limit_config.py, sized like the billable dev:conversations LLM-create path and tighter than the dev:conversations_read list limit (60/hr) the endpoint would otherwise have used.
  • Enforced in a dedicated dependency get_uid_with_conversations_read_ask (conversations:read scope + dev:ask), fail-closed on the per-app/key identity via the shared dev-API check_api_key_rate_limit. A leaked or overused key is capped, and a Redis outage denies rather than opens.
  • Route-policy manifest updated: rate_limit nonedev:ask / api_key / fail_closed / dependency.

Tests

  • backend/tests/unit/test_dev_ask_endpoint.py — returns a grounded, cited answer from the retrieved conversations; makes no LLM call when nothing relevant matches; and (regression for the review) asserts the endpoint is bound to the rate-limited dependency, which enforces exactly dev:ask and is tighter than the list-read limit.
  • sdks/python-cli/tests/test_ask.py — the command POSTs the question and renders the answer + sources; --json emits the raw payload.

Regenerated the public + app-client OpenAPI contracts and the derived TS + Swift clients for the new endpoint.

Product invariants affected

none

Failure-Class: none

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

@Git-on-my-level Git-on-my-level added needs-maintainer-review Needs a human maintainer to sign off before merge security-review Touches auth, provider routing, secrets, or security-sensitive surfaces needs-tests PR introduces logic that should be covered by tests labels Jul 22, 2026

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for putting this together — the feature is coherent with the Developer API/CLI direction, and I like that it reuses the existing conversation search + cited RAG path rather than inventing a second retrieval stack.

I do think this needs changes before maintainer sign-off:

  • POST /v1/dev/user/ask is a new public Developer API endpoint that can invoke an LLM on demand, but the route policy explicitly declares rate_limit: none and the implementation does not add a quota/cost guard. Even with conversations:read auth, a leaked or overused developer key could turn this into an unbounded billable endpoint over sensitive user conversation data. Please put this behind an appropriate per-user/key rate limit or existing usage budget before merge.
  • The current check run has Backend unit suite failing. It looks outside this endpoint from the log I inspected, but this PR should either get a green rerun or have maintainers mark the failure as pre-existing before merge.

Given the endpoint exposes sensitive conversation-derived answers and a billable LLM path, this should get human maintainer review for the product/security posture even after those fixes.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

Thanks for the review — both points addressed.

1. Rate limit / cost guard. You're right that riding dev:conversations_read (a cheap list-read limit, 60/hr) left the billable LLM path effectively unbounded. The endpoint now carries a dedicated per-key budget:

  • New dev:ask policy — 25/hour, per app/key identity — in utils/rate_limit_config.py, sized like the billable dev:conversations LLM-create path and strictly tighter than the list-read limit.
  • Enforced in a dedicated auth dependency get_uid_with_conversations_read_ask (conversations:read scope + dev:ask), fail-closed via the shared dev-API check_api_key_rate_limit (_enforce_rate_limit(..., fail_closed=True)) — a leaked/overused key is capped, and a Redis outage denies rather than opens.
  • Route-policy manifest updated to match reality: rate_limit nonedev:ask / api_key / fail_closed / dependency.
  • Regression test (test_ask_endpoint_is_bound_to_the_dev_ask_rate_limited_dependency) asserts the endpoint is wired to the rate-limited dependency, that it enforces exactly dev:ask, and that dev:ask is tighter than the list-read limit — so a future edit can't silently drop it back to the loose read budget.

2. Failing Backend unit suite. It was a stale-base failure outside this endpoint (as your log inspection suggested). Rebased onto latest main; make preflight passes 28/28 locally and the fresh CI run should be green.

Agreed this warrants human maintainer review for the product/security posture given the sensitive-data + billable-LLM surface — ready for that look.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dev-api feature — approved; not auto-merged (feature, outside desktop)

@Git-on-my-level

Copy link
Copy Markdown
Collaborator

Thanks for the follow-up. I re-reviewed the current head (9ecd1d0d) and the dedicated dev:ask budget plus fail-closed dependency/route-policy regression test addresses the main cost-guard concern from my earlier review.

I’m not dismissing the prior change request yet because this head is still not green: Backend unit suite is failing in tests/unit/test_desktop_release_scripts.py::test_codemagic_produces_canonical_app_and_strictly_verifiable_dmg, and Hermetic Backend E2E is failing in TestSurfaceDefaultAccessMatrix::test_surface_excludes_archive_and_respects_grants[agent_tools] (which also fails the Backend Hermetic Merge Gate). These look outside the ask endpoint, but this PR still needs either a green rerun or maintainer confirmation that the failures are unrelated/pre-existing before merge.

Given this exposes a new public Developer API surface over sensitive conversation data and a billable RAG call path, I’d still keep needs-maintainer-review/security-review on it for human product/security sign-off even after CI is resolved.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

Thanks — agreed both failures are outside this endpoint, and I checked rather than asserting it. Evidence:

test_desktop_release_scripts.py::test_codemagic_produces_canonical_app_and_strictly_verifiable_dmg — I ran it against a clean checkout of current main (no changes from this PR):

pytest backend/tests/unit/test_desktop_release_scripts.py::test_codemagic_produces_canonical_app_and_strictly_verifiable_dmg
→ 1 passed

So it isn't a defect in this diff — it was failing against the older base this PR sat on, and main has since moved past it. This PR is backend dev-API + CLI only and touches no desktop release scripts.

TestSurfaceDefaultAccessMatrix::test_surface_excludes_archive_and_respects_grants[agent_tools] — a canonical memory surface/access-matrix assertion (short-term memory not surfaced). This PR adds a conversations-grounded ask endpoint and touches no memory surface or grant path. The same failure is currently hitting other unrelated PRs (it also appeared on #10262, a conversation-merge change), which points at main, not either diff.

I've now rebased onto latest main so both run against current state — the release-scripts case should clear outright, and the memory-E2E case will show whether it's still live on main independent of this PR. test_dev_ask_endpoint.py → 3 passed and make preflight → 28/28 locally on the rebased head.

Agreed on keeping needs-maintainer-review / security-review for the human product/security pass on the sensitive-conversation + billable-RAG surface.

@aryanorastar

Copy link
Copy Markdown
Contributor Author

Both are handled in the current tip (7fede1a28c) — I believe the review predates the rate-limit commit:

1. Rate limit (the billable-endpoint concern). POST /v1/dev/user/ask is bound to get_uid_with_conversations_read_ask (dependencies.py:317), which requires conversations:read and enforces a dedicated dev:ask budget of 25 requests/hour per key (rate_limit_config.py) — a tight per-key cap on the LLM path, distinct from the cheap list-read limit. The route policy declares policy_name: dev:ask, not none. Test test_ask_endpoint_is_bound_to_the_dev_ask_rate_limited_dependency pins that binding and that dev:ask is strictly tighter than the list-read limit.

2. Backend unit suite. As you suspected, that's outside this endpoint — it's the pre-existing app-client OpenAPI drift from the memories baseline toggle (#8728), fixed in #10482. /v1/dev/user/ask is in the committed public spec and export_openapi.py --surface public --check passes on this branch.

Agree it warrants human review for the product/security posture given the sensitive-data + billable-LLM surface — flagging @Git-on-my-level for that. Nothing code-side outstanding that I can see; happy to adjust the cap if 25/hr is the wrong number.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the quick update here. The dedicated dev:ask rate limit, fail-closed dependency wiring, route-policy entry, and regression test address the original unbounded billable-LLM/cost-guard concern from my first review.

I do need to keep this in changes-requested state on the current head (7fede1a) because the generated API client/spec artifacts are still stale:

  • Public Developer API contract fails at backend/scripts/generate_ts_openapi_types.py --check, reporting the four TS generated clients as stale (desktop/windows/.../omiApi.generated.ts, web/app/..., web/admin/..., and web/personas-open-source/...).
  • Backend unit suite also fails the generated-client tests: tests/unit/test_app_client_swift_generator.py::test_swift_dto_file_is_generated_from_app_client_openapi and tests/unit/test_app_client_ts_generator.py::test_typescript_schema_types_are_generated_from_app_client_openapi.
  • The Desktop Swift aggregate is red because the Swift test suite was cancelled; the backend unit output also indicates desktop/macos/Desktop/Sources/Generated/OmiApi.generated.swift is out of date and should be regenerated with python generate_swift_openapi_types.py.

Please regenerate the checked-in TS/Swift API client artifacts from the current OpenAPI output and rerun the contract/unit checks. After that, this still needs human maintainer/security-product sign-off before merge because it adds a public Developer API + CLI surface over sensitive conversation data and a billable cited-RAG path, but I do not see the earlier rate-limit blocker remaining.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

Answer a natural-language question grounded in the user's own conversations, from
the terminal or any Developer-API caller — no desktop app required.

Backend: POST /v1/dev/user/ask (conversations:read scope) semantically searches the
caller's conversations (search_conversations) and synthesizes a cited answer from the
most relevant ones (qa_rag) — the same retrieval + RAG the chat surface uses. Read-only;
locked conversations are excluded by the search layer; no LLM call when nothing relevant
is found.

CLI: `omi ask "<question>"` (with --limit/--timezone/--json), rendering the answer plus
its conversation sources.

Regenerated the public + app-client OpenAPI contracts and the derived TS clients.

Verified: pytest test_dev_ask_endpoint.py (backend, test.sh-isolated) + sdks/python-cli
tests/test_ask.py (CLI, real env) pass.

Failure-Class: none

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@aryanorastar

Copy link
Copy Markdown
Contributor Author

Regenerated the stale client artifacts — done on the rebased head.

Verified with the .openapi-venv + full pytest venv:

generate_{dart,ts,swift}_openapi_types --check  → all OK
export_openapi --check {public,app-client,integration}  → all up to date
test_dev_ask_endpoint (3) + test_app_client_swift_generator + test_app_client_ts_generator → 18 passed
ratchet check (developer.py) → OK

That clears the generate_ts_openapi_types.py --check, the Swift/TS generator unit tests, and the OpenAPI-contract failure you flagged. The desktop-swiftlint red is a local sourcekitd tooling issue on my side only (Sources/Generated/ is swiftlint-excluded); CI runs the real linter. Still yours for the human product/security pass on the sensitive-conversation + billable-RAG surface — no code blocker outstanding that I can see.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

needs-maintainer-review Needs a human maintainer to sign off before merge needs-tests PR introduces logic that should be covered by tests security-review Touches auth, provider routing, secrets, or security-sensitive surfaces

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants