Commit Graph

27 Commits

Author SHA1 Message Date
Vineeth Voruganti be7ad9d919
Scopes Phase 2b: `scope` option on chat, representation, context, and search (#897)
* fix: apply session scoping to all working-representation query paths

session_name was only applied to the recent-documents query in
RepresentationManager; the semantic and most-derived paths ignored it,
so limit_to_session leaked cross-session conclusions into perspectives.

- Thread a session allowlist (session_names) uniformly through all
  three query paths; pushed down to pgvector and external vector stores
- Accept a list so the upcoming session-allowlist API reuses this path
- Fail closed on an empty allowlist (downstream stores drop empty IN
  clauses, which would silently widen scope)

Fixes DEV-1994

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: bare-list membership sugar in the filter DSL

{"session_id": ["s1", "s2"]} is now shorthand for
{"session_id": {"in": [...]}} on regular columns, generically
(peer_id, etc.). JSONB metadata columns are excluded — a bare list
there keeps JSONB containment semantics, unchanged.

Previously a bare list on a regular column compiled to a type-mismatched
equality that matched nothing, so this is strictly additive.

Also translates the same shape in the turbopuffer/lancedb filter
builders, and fixes lancedb dropping empty IN clauses (fail-open) —
an empty membership list now emits an always-false condition.

Groundwork for DEV-1995 (session allowlist via the existing filters
DSL, no new API params)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: session allowlist on dialectic and representation via filters

Adds a constrained 'filters' body to peer.chat and /representation —
the same DSL search and conclusions already accept, supporting only the
session_id key (a session id, a bare list, or {"in": [...]}).
Unsupported keys and shapes are rejected with 422, never silently
ignored. Composes with session_id (must be included in the allowlist
when both are given). Capped at 1,000 sessions per request.

Enforcement is uniform at every recall chokepoint, fail-closed:
- dialectic prefetch + search_memory: conclusion recall restricted to
  the allowlist; dream docs (session_name IS NULL) excluded
- message tools (search/grep/date-range/temporal/context/history):
  strict intersection of allowlist and observer session membership
- get_reasoning_chain: unavailable under an allowlist (chains traverse
  provenance across sessions and cannot be scoped without leaking)
- empty allowlist short-circuits to empty results everywhere

Auth: workspace keys pass the allowlist as-given; peer-scoped JWTs must
be a member of every allowlisted session (403 otherwise), mirroring the
existing single-session check.

Fixes DEV-1995

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: reserve scope__ peer namespace with kind flag and guardrails

Introduce the scope peer namespace (scope__<name>) and the authoritative
{"kind": "scope"} configuration flag, plus the server-side guardrails:

- src/utils/scopes.py: single source of truth for the prefix, kind flag,
  and name helpers (scope_peer_name / is_scope_peer_name /
  scope_name_from_peer / validate_no_scope_peer_names)
- reject reserved-prefix names on peer get-or-create (422)
- reject scope peers as message authors in crud.create_messages (422)
- reject scope peers as chat/representation targets (422); a scope peer
  as the path-level observer is deferred to Phase 2b
- reject scope peers on the generic session-peer add/set/remove routes
  and the session-create peers mapping (422, directing to scopes routes)
- peers.list excludes scope peers by default; new PeerGet.kind option
  ("scope" | "all") switches the view via a configuration JSONB filter
- schemas: Scope / ScopeCreate / ScopeSessions(Add) and
  SessionCreate.scopes (unprefixed scope names, validated)

Part of DEV-1997 (Scopes RFC DEV-1970).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat: add scopes CRUD routes and session-create scopes wiring

New /v3/workspaces/{workspace_id}/scopes facade (workspace-level auth;
peer- and session-scoped keys are rejected):

  POST   ""                                   create-or-get (201/200)
  POST   /list                                 paginated scope list
  GET    /{scope_id}                           single scope
  POST   /{scope_id}/sessions                  add memberships
  DELETE /{scope_id}/sessions/{session_id}     remove membership
  GET    /{scope_id}/sessions                  list member session ids

- crud/scope.py: get_or_create_scopes stamps the backing peer with
  {"kind": "scope", "observe_me": false} and refuses to adopt a
  legacy peer occupying the reserved name without the flag (409)
- memberships are session_peers rows with observe_others=true /
  observe_me=false — identical to a hand-built observer peer
- SessionCreate.scopes: create-or-get each scope peer and add the
  membership at session creation (the no-backfill common path)
- crud/session.py: public upsert_session_peers wrapper so the facade
  bypasses the route-level guardrails without reaching into privates

Backfill of pre-existing documents and reconciliation on removal land in
DEV-1999; membership only affects messages ingested after the change.

Part of DEV-1997 (Scopes RFC DEV-1970).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test: cover scopes facade, guardrails, and observer semantics

- create-or-get idempotency, list/get, name validation, legacy-collision
  rejection (409), auth scoping (workspace key ok, peer/session keys 401)
- reserved prefix rejected on peer create; peers.list kind filtering
- scope peers rejected as message authors, chat/representation targets,
  and on the generic session-peer routes
- membership add/list/remove with observe_others=true / observe_me=false
  row shape asserted via DB, and facade-less equivalence with a
  hand-built observer peer
- end-to-end litmus: after adding a session to a scope, the deriver
  enqueue fan-out includes the scope peer as an observer
- session creation with scopes: [a, b] creates both memberships

Part of DEV-1997 (Scopes RFC DEV-1970).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(scopes): scope resolution helper and read-route schemas

Add resolve_scope_peers (crud/scope.py) mapping unprefixed scope names to
their backing scope peers, 404 when missing and 422 when a non-scope peer
squats the reserved name. Add the `scope` option to DialecticOptions and
PeerRepresentationGet, and a WorkspaceMessageSearchOptions with `scope`.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* feat(scopes): wire the `scope` read option into the routes

Chat and representation: a single scope swaps the observer to the scope
peer (recall confined to the scoped collection and the scope's sessions by
existing observer semantics); a scope list resolves to the union of member
sessions and rides the DEV-1995 dynamic allowlist arm with the path peer as
observer. `scope` is mutually exclusive with `filters`/`session_id` (422)
and requires a workspace/admin key (403 for peer-scoped JWTs).

Session context: `scope` swaps the perspective source for both the working
representation and the peer-card fetch. Workspace search: `scope` injects
the scope's session set into the message-search filter (empty scope -> no
results). Close the carry-over guardrail gap: scope peers are rejected as
peer_target/peer_perspective in session context and as the peer/target in
GET /peers/{id}/context.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* test(scopes): cover the `scope` read option end-to-end

Validation (404/422/403), single-scope observer swap vs. union allowlist,
scoped representation and session-context (scoped collection + scoped card),
workspace search restricted to a scope's sessions, and guardrail closure for
scope peers on the peer/session context surfaces. Patch the workspaces
tracked_db import site so scope resolution reads the per-test database.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(crud): preserve cache invalidation across get_or_create retry

`get_or_create_peers` and `get_or_create_scopes` mutate existing rows, then
insert new ones inside `db.begin_nested()`. When a concurrent writer creates
one of those rows first, the insert raises IntegrityError and the function
retries.

`begin_nested()` autoflushes the pending UPDATEs *before* opening the
savepoint, so the rollback neither undoes them nor expires the now-clean ORM
state. The retry then compared already-updated values, found no change, and
dropped those peers from `changed_peers` — skipping the cache purge while the
row change committed anyway, leaving entries stale until the 300s TTL.

Carry the mutated names into the retry via `_pending_invalidation` so the
purge cannot be lost.

The scopes facade mirrors `get_or_create_peers`, so both copies carried this.
The peer path is pre-existing and runs on every message ingest.

Also add the missing /v3 prefix to `_SCOPES_ROUTE_GUIDANCE`, which pointed
callers at a 404.

Adds tests/crud/test_get_or_create_retry_invalidation.py, which drives a real
racing session and fails without this change.

* fix(scopes): make scope identity unforgeable, unblock non-pattern peer names

Three coupled changes to the scopes facade.

1. `PeerCreate` no longer gates internal lookups. It exists to validate a new,
   user-supplied peer id at the API boundary, but crud used it as a DTO for
   names that already exist, so any name outside RESOURCE_NAME_PATTERN raised a
   raw pydantic ValidationError — which is not a HonchoException, so it fell
   through to the catch-all handler as an HTTP 500. Adds `PeerSpec` (same
   fields, no charset pattern) as `PeerCreate`'s base, widens
   `get_or_create_peers` to accept it, and changes `get_peer` to take a plain
   str. All 13 construction sites converted; the create route keeps full
   validation.

   This unbreaks the Dreamer: DreamScheduler passes `collection.observer`
   straight into the specialist preflight, and scope peers have
   `observe_others=true`, so every `(scope.x, peer)` dream died there — the
   feature scopes exist to enable. It also fixes a pre-existing bug unrelated
   to scopes: a peer named `alice.smith` (legal before d429de0e5338, which
   validated names by length alone) 500s on message create, session peer add,
   and peer update.

2. The `kind` flag moves from `configuration` to `internal_metadata`.
   `configuration` is user-writable — `PeerCreate`/`PeerUpdate` accept a
   free-form dict and `update_peer` replaces it wholesale — so a legitimate
   `{"observe_me": true}` update silently dropped the flag, and a forged
   `{"kind": "scope"}` injected an ordinary peer into `POST /scopes/list`.
   `internal_metadata` appears in no API schema. `observe_me: false` stays in
   `configuration`, where it belongs.

3. Scope identity requires prefix AND flag, via `is_scope_peer()` and
   `scope_peer_clause()`. Neither half is forgeable: the prefix sits outside
   RESOURCE_NAME_PATTERN, `internal_metadata` is unreachable. Usage-site guards
   become flag-based so a legacy peer merely occupying the namespace keeps
   working rather than 422-ing on its own traffic; peer create and update stay
   name-based, since those must stop new names entering the namespace.
   `update_peer` now returns 422 instead of 500.

Also swaps the reserved prefix from `scope__` to `scope.`: `_` is inside
RESOURCE_NAME_PATTERN, so any tenant could already own a `scope__x` peer.

No DB migration — `internal_metadata` already exists on `peers`, and no scope
peers exist in any deployment yet.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): validate peer names on create, close namespace squatting and upsert race

Addresses three review findings against 48047a6a.

1. `PeerSpec` let API callers create invalid and reserved-prefix peers.
   Widening `get_or_create_peers` to accept a pattern-free schema fixed the
   lookup 500s but also removed validation from the *insert* path, and
   request-controlled names reach it via message authors, session peer maps, and
   the chat observer path — none of which carry a charset pattern of their own.
   Confirmed: `POST /sessions/{id}/messages` with `peer_id: "scope.x"` returned
   201 and minted an unflagged squatter, after which `POST /scopes {id: x}` was
   permanently 409-blocked — namespace denial of service by any caller able to
   post a message. `peer_id: "not a valid name!@#"` was likewise created.

   Fixed by validating only names about to be INSERTed
   (`_validate_new_peer_names`), so already-existing names — legacy dotted
   names, scope peers — still resolve without a spurious 422. That keeps the
   Dreamer fix intact, since it reads through `get_peer`.

2. Existing reserved-prefix squatters could not be updated. The name-based guard
   on `PUT /peers/{peer_id}` refused every `scope.` name, contradicting the
   invariant that an unflagged squatter stays a normal peer. Now flag-based, so
   behavior is three-way: a real scope is refused, an existing unflagged peer
   updates, and a missing reserved-prefix name is refused by (1) rather than
   minted.

3. Scope checks raced with get-or-create and the membership upsert. The
   route-level guards run before peers are resolved, so a scope created
   concurrently in that window would be attached by the generic path with a
   default `SessionPeerConfig()`, clobbering its observer membership config.
   Adds `_reject_resolved_scope_peers`, which runs on the resolved rows in the
   same transaction as the upsert — no window, no extra query. The early checks
   stay for better error messages.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): guard scope membership config, move checks to the mutation point

Addresses a second review pass against 10655792.

1. Scope membership configuration was directly user-mutable.
   `PUT /sessions/{id}/peers/{peer_id}/config` had no scope guard at all, and
   `crud.set_peer_config` resolved the peer only to discard the row. Confirmed:
   posting `{"observe_others": false, "observe_me": true}` for a scope returned
   204 and persisted, which silently stops all fan-out into the scope and makes
   Honcho form a representation *of* a scope — neither of which is reachable
   through the facade. Deterministic, no race required. Now checked on the row
   `get_peer` already returns, so it costs nothing and cannot race.

2. Empty and over-long names were still 500s. Removing the charset pattern from
   `PeerSpec` fixed one trap but left its length bounds, and request-bound peer
   names carry no length limits of their own — so `peer_id: ""` or a 513-char
   name reached `PeerSpec(...)` and raised a raw pydantic ValidationError that
   the catch-all turned into a 500. `PeerSpec` now carries no constraints at all
   (matching its documented purpose) and every rule for a new name lives in
   `_validate_new_peer_names` on the insert path.

3. Resolved-row protection generalized. The previous pass applied it only to
   membership upserts, leaving check-then-use windows elsewhere: peer update
   could have a concurrently-created scope's configuration replaced wholesale
   (create-path validation does not fire for a peer that now exists), the chat
   observer get-or-create could resolve a fresh scope as its observer, and the
   generic session-peer removal could silently detach a scope from its sessions.
   Each now inspects the resolved peer immediately before acting; the redundant
   name-level guard on the update route is dropped in favor of the race-free one.

`remove_peers_from_session` grows an internal `_allow_scope_peers` flag because
the scopes facade ends membership through that same path and must not be blocked
by its own guard.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* test(scopes): enumerate every peer-touching route against a scope policy

Three review passes each found the same class of defect: a route nobody had
checked, rather than logic that was subtly wrong. One of them — `PUT
/sessions/{id}/peers/{id}/config`, which let any caller set a scope to
`observe_others=false` and silently stop all fan-out into it — predated this work
entirely, because the guardrail set was assembled guardrail-by-guardrail instead
of derived from the route list. Sampling review cannot close that kind of gap;
enumeration can.

Derives every route through which a peer name can reach the system and requires
each to be classified as GUARDED or EXEMPT-with-a-reason, so a newly added
peer-touching route fails the suite until someone classifies it. Detection is the
union of path shape and a walk of the dependant tree (including sub-dependency
`Form(...)` params and nested request-body models), because neither signal alone
suffices: parameter names miss `POST /sessions/{id}/peers`, whose peer names are
dict keys, and path shape misses `messages/upload`, whose `peer_id` arrives as a
form field behind a parser dependency.

Both invariants are then asserted behaviorally, by calling the routes rather than
inspecting annotations — the guards deliberately live in crud, which is what makes
`messages/upload` guarded for free via `crud.create_messages`:

- a real scope is refused on all 11 guarded routes, and the rejection must name
  the scope, so an unrelated 422 (a malformed body) cannot pass the assertion;
- an *unflagged* peer merely occupying the reserved namespace is unaffected. That
  half regressed once already when `update_peer` used a name-based check.

Mutation-tested all three failure modes: disabling the `set_peer_config` guard
fails the guarded test naming that route; regressing `update_peer` to name-based
fails the squatter test; adding an unclassified peer route fails the enumeration.

Covers the HTTP surface only. Peer names also reach the system through the
deriver, dreamer, and queue, which have no route table to enumerate — noted in
the module docstring rather than implied to be covered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): refuse a scope in every observed position; enumerate per position

Addresses a fourth review pass against 62681e4d. The headline finding is that the
previous commit's enumeration test had the wrong *model*, not a missing entry.

1. Manual conclusions could create knowledge about a scope. `POST /conclusions`
   validated only that observer_id and observed_id exist, so a scope as
   `observed_id` persisted a conclusion about a peer carrying observe_me=false and
   created an (observer, scope) collection for it. Confirmed: 201, and it read
   back. `POST /schedule_dream` had the same hole via `observed`.

   The fix is positional, because the invariant is:

       A scope may be an OBSERVER. A scope may never be OBSERVED.

   A scope as `observer_id` is how scoped conclusions are stored and must keep
   working (verified still 201); as `observed_id` it is now refused. Same split
   applied to schedule_dream `observed`, the peer-card `target` (which also covers
   a scope's self-card, since target-omitted collapses observed to peer_id), and
   session-context `peer_target`.

2. Chat target and both representation roles kept check-to-use races. Only the
   chat path-level observer was re-checked on its resolved row; the target was
   checked by name and then resolved without inspecting scope identity. Both are
   now checked at the dialectic preflight, where observer and observed are already
   resolved — an absent name has already failed by then, and an existing squatter
   cannot retroactively become a scope.

3. Generic membership removal was still racy. The adjacent SELECT narrowed the
   window but could not close it under READ COMMITTED. The UPDATE now carries its
   own correlated NOT EXISTS against scope_peer_clause(), so Postgres evaluates
   the exclusion as part of the statement and a scope committed after the advisory
   check still cannot be detached.

4. New-name validation ran after the name reached Postgres. A NUL byte passed the
   request schemas and PeerSpec, then raised psycopg.DataError inside the lookup —
   a 500. Values that cannot correspond to a stored row by construction (NUL
   bytes, over-length names) are now refused before the query.
   (Over-length names already returned 422; only the wasted query was real there.)

The enumeration test is rekeyed from (method, path) to (method, path, position).
A binary per-route verdict cannot express finding 1 at all: `POST /conclusions` is
one route with two positions and opposite verdicts. Detection widens to observer /
observed / target / peer_target / peer_perspective, which surfaced four routes the
previous version never saw — conclusions, schedule_dream, queue/status, and
session context.

Also registers `src.routers.workspaces.tracked_db` in the conftest patch list; the
new guard there would otherwise have run against the real configured database
instead of the per-test one.

Mutation-tested: disabling the conclusions observed-guard fails the positional
test naming that position; adding an unclassified `observed_id` param fails
enumeration.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): refuse future scopes in observed positions, preserve scope membership

Addresses a fifth review pass against 14136e5b. All six findings reproduced
locally before fixing.

1. High — generic peer replacement removed scope memberships.
   `set_peers_for_session` soft-deleted every active SessionPeer row, and the
   request-level guard only inspected names *present* in the replacement map. A
   caller detached a scope by simply omitting it, never naming it — so no
   request-level guard could ever see it. Reproduced: scope sessions went
   ['<id>'] -> [] on a 200. The exclusion now lives in the UPDATE itself
   (correlated NOT EXISTS against scope_peer_clause), so replacement means
   "replace ordinary peers" regardless of request contents or concurrent creation.

2. High — peer cards could be pre-seeded for future scopes.
   `set_peer_card` resolves only the observer and writes a JSONB key derived from
   an unchecked observed name, and the route guard rejected only *existing*
   flagged scopes. Reproduced: PUT card with target=scope.<missing> returned 200,
   creating that scope then returned 201, and the card described the real scope.

3. High — dreams could be queued for future scopes.
   The route checked `observed` in a read-only session that closed before
   `enqueue_dream`, and a missing reserved name passes any is-it-a-scope check.
   Reproduced: 204 with observed=scope.<missing>.

   2 and 3 share a root cause, so they share a fix: a new `reject_scope_observed`
   that is stricter than `reject_scope_peers` in exactly one case — a *missing*
   reserved name is refused, because nothing on these paths creates the peer, so
   nothing else would ever catch it. Existing unflagged squatters still pass.
   Both guards moved to the mutation point: card validation into
   `crud.set_peer_card` (same transaction as the JSONB write, so Dreamer and
   agent-tool callers are covered), dream validation into `enqueue_dream` (same
   transaction as the queue insert). The redundant route-level checks are dropped
   rather than left as weaker duplicates.

4. Medium — prefixed NUL names still reached PostgreSQL.
   `reject_scope_peers` filtered for the reserved prefix and sent matches to a
   text comparison, so "scope.future\0name" raised psycopg.DataError — a 500.
   Both guards now share `_reserved_name_candidates`, which materializes the input
   once and rejects impossible values before any SQL. Materializing matters
   independently: the message-author path passes a generator, and validation
   iterates separately from the prefix filter, so a generator would be
   half-consumed. `_reject_impossible_peer_names` now takes a Collection so the
   type checker enforces that.

5. Medium — representation kept a check-to-use race.
   The previous commit claimed both representation roles were rechecked after
   resolution; that was wrong — only the dialectic preflight got that check, and
   the representation route never goes through it. It now opens one short
   read-only session *after* the embedding call, checks both positions, and passes
   that same session to `get_working_representation`, so no connection is held
   across external work and a scope committed later cannot have conclusions in the
   collection being read.

6. Low — policy coverage was not exhaustive. `sender_id` reaches CRUD as
   `observed` but was missing from the detected parameter set. ALLOW cases could
   also not carry builders, so the suite never proved the other half of the
   contract — that legitimate scope *observers* keep working, which a guard
   rejecting scopes everywhere would satisfy. Both fixed; observer positions on
   conclusions, dreams, cards, session context and queue status are now asserted
   behaviorally.

Deliberately not implemented: the scope-creation backstop scanning for
pre-existing card keys and queue items naming a future backing peer. Reasoning is
recorded in `get_or_create_scopes` — no new such state can be created now, any
pre-existing row is coincidental since `scope.` was never a meaningful namespace,
the consequence is inert, and detecting card keys means a full table scan per
scope creation.

Mutation-tested each new guard: removing the replacement exclusion fails both
membership-preservation tests; weakening either observed guard to existing-only
fails the pre-seeding tests.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): exclude scope memberships from the session observer limit

Scope memberships carry observe_others=true, so every scope counted against
SESSION_OBSERVERS_LIMIT (default 10) — capping scopes-per-session at the limit
minus the session's real observers, and reporting the failure as
`400 Cannot create session <name> with 11 observers. ... Observers are peers
with 'observe_others' set to true.` on a membership call. Wrong on three counts:
the ceiling is undocumented and contradicts RFC §5.1 ("sessions belong to any
number of scopes"), the message describes session creation, and it leaks the
word "observer" through a facade whose entire job is hiding observers (RFC
goal 5). The limit exists to bound per-observer deriver fan-out for real peers;
a scope costs document rows, not LLM calls (RFC §5.2), so it does not belong in
that budget.

Excluded from both halves of the check in `_get_or_add_peers_to_session`: the
incoming names via a flag-based lookup, existing memberships via a correlated
NOT EXISTS on `scope_peer_clause()` — the same pattern the replacement and
removal paths already use, so the exclusion holds regardless of concurrent
scope creation. The early `count_observers_in_config(session.peer_names)` check
in `get_or_create_session` is left alone: `peer_names` cannot contain a scope,
and `scopes` is a separate field.

`reject_scope_peers` is split into a `scope_peer_names()` query helper plus a
two-line raiser so the observer count reuses the authoritative name-AND-flag
predicate instead of growing a third copy of it. Still costs nothing on the
common path — no reserved-prefix name in the input means no query at all.

Also caps `SessionCreate.scopes` at 100, matching `ScopeSessionsAdd.session_ids`.
This belongs in the same commit: the observer limit was the only thing bounding
that list, so removing it turns an unbounded `scopes` array into a peer row and
a membership row per element, committed — the single-request path to the
cardinality anti-pattern RFC §8 warns about. Partly answers OQ6: no per-session
cap, 100 per request.

Tests: a session joins SESSION_OBSERVERS_LIMIT + 2 scopes through both the
facade and session creation; real observers over the limit still 400, so the
carve-out cannot quietly disable the limit; 101 scopes is a 422.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): skip semantic retrieval when the embedding precompute failed

Addresses three open review comments.

1. Major — the representation read could embed inside its DB session. The
   route's precompute is suppressed, and both
   `RepresentationManager.get_working_representation` and
   `crud.query_documents` fall back to embedding when a query arrives without
   one, so a failed precompute meant an external call inside the read session
   this branch opens for the scope re-check — the connection-holding rule the
   route's own comment claimed to satisfy. The innermost fallback also only
   catches ValueError, so a provider outage surfaced as a 500. The semantic
   query is now passed only when an embedding exists, degrading to
   derived+recent retrieval. (`crud.query_documents` embedding inside a caller's
   session predates this branch and is left alone.)

2. Minor — `test_resolved_scope_peer_rejected_at_membership_upsert` described a
   race it does not perform. It creates an already-flagged scope and calls crud
   directly; the unflagged → flagged transition is not simulated. Docstring now
   says what the test actually pins.

3. Minor — `test_empty_replacement_preserves_scope_membership` asserted only
   half its docstring. It passed if the empty PUT left every ordinary
   membership intact; now asserts the ordinary peer's left_at is set.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(scopes): close auth and observed-position gaps, paginate membership

Review response for #884.

Security:
- gate `SessionCreate.scopes` behind a workspace-level key; the session-create
  route is self-authorizing, so a peer- or session-scoped token could mint scope
  peers and join sessions to scopes it had no access to via `POST /scopes`
- refuse a reserved-but-nonexistent name in two observed positions that used the
  permissive guard: chat `target` and session-context `peer_target`. Both let a
  caller act on `scope.X` before it existed, then create the scope

Facade:
- exclude scope peers from `GET /sessions/{id}/peers` and refuse the membership
  -config read for a real scope, matching its write side
- replace `GET /scopes/{id}/sessions` with `POST /scopes/{id}/sessions/list`
  returning `Page[Session]`; the add route now returns 204. Membership was
  unbounded on both, while every other list surface paginates
- rename `crud.get_scope` to `get_scope_or_raise`

Tests:
- add a missing-name axis to the route-policy table (`Case.refuse_missing`), which
  is what surfaced the two guard gaps above
- delete 14 hand-written tests the table now enumerates; 52 -> 39 functions in
  test_scopes.py with more cases covered
- tighten the squatter assertion from `!= 422` to `< 400`, which was passing on 5xx
- assert the FastAPI-internals traversal still derives positions, so a framework
  upgrade can't silently empty the suite

Docs:
- drop internal ticket and RFC references from the published OpenAPI descriptions
  and surrounding comments; state the behavior instead
- move implementation reasoning out of the `PUT /peers/{id}` docstring, which
  FastAPI publishes, into a comment

* test(scopes): assert exact statuses for permissive missing-name cases

Follow-up review pass on #884.

- add `Case.missing_status` so a permissive missing-name position asserts the
  status it should actually get (404, or 200 for the no-op removal) instead of
  `!= 422`, which also passed on a 5xx — the same hole already closed in the
  squatter assertion
- require it whenever `refuse_missing` is False, and require its absence when
  True, so the policy table can't drift from the assertion
- repoint a stale allow-reason at POST /scopes/{scope_id}/sessions/list; the GET
  it named was removed
- document the membership list's ordering under `reverse`

* fix: Address Coderabbit Comments

* fix: Address more coderabbit comments

* chore: remove ai artifacting

* fix: remove unused fakeredis client in lieu of in-memory taskless cache for test suite

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-12 18:34:55 -04:00
adavyas 0cf63c10da
feat(api): restore reverse pagination (#685)
* feat(api): restore v3 reverse pagination

* docs: add reverse pagination docstrings

* docs: document session reverse parameter

* fix: add fallback column for ties

* refactor: tighten reverse query typing

* chore: pre-commit styling

* chore(tests): Add additional validation tests and update changelogs

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-05-21 13:40:47 -04:00
doria e9d8b8759d
refactor: update semantic search parameter from `last_user_message` to `search_query` across documentation and SDKs (#341)
* refactor: update semantic search parameter from `last_user_message` to `search_query` across documentation and SDKs

- Changed references in documentation and code to use `search_query` instead of `last_user_message` for fetching semantically relevant observations and conclusions.
- Updated related function signatures and descriptions in Python and TypeScript SDKs to reflect this change.
- Adjusted tests to ensure compatibility with the new parameter naming.

* chore: openapi v3 formatted how we like it

* fix: reorder docs, update examples in README, update skills

* fix: message type option in sdk reference

* chore: update remaining getcontext and representation language

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-01-23 13:11:20 -05:00
doria dce96889bc
feat: honcho 3.0, sdks 2.0, excise stainless, update v3 docs, changelogs (#331)
* chore: 3.0 honcho and 2.0 sdks changelog

fix: use PeerContextResponse in peer.ts

* chore: move docs to /v3/, build SDKs

* chore: code review

* feat: [WIP] migrate away from stainless in typescript sdk

* chore: move api from /v2/ to /v3/

* feat: no-stainless typescript with real tests

* feat: migrate python sdk off of stainless

* feat: clean typescript sdk

* chore: add tests for ts http client

* fix: rewrite entire python sdk in new format, update typescript sdk to use `configuration` not `config` for consistency with API

* fix: clean up SDKs, synchronize

* chore: update sdk examples

* chore: update OpenAPI documentation and SDK examples to reflect changes

* fix: better test

* fix: install deps in test runner, improve robustness of streaming in sdk, coderabbit nits

* fix: standardize around camelCase in TS SDK

* refactor: update configuration handling in SDKs to use typed models for workspace, session, and peer configurations

* docs: clarify queue status usage and remove polling methods from SDKs

add claude skills for migrations

* chore: fix links in docs

* feat: add deriver flush mode to bypass batch token threshold

- Introduced `is_deriver_flush_enabled` function to check if flush mode is active.
- Updated `QueueManager` to conditionally apply batch token thresholds based on flush mode.
- Enhanced `UnifiedTestExecutor` to enable flush mode via Redis.
- Added `flush` parameter to test cases to facilitate testing of flush mode behavior.
- Updated various test cases to utilize the new flush functionality.

* feat: implement schedule_dream functionality in SDKs, use in unified test runner

- Added `schedule_dream` method to both Python and TypeScript SDKs for scheduling dream tasks.
- Updated HTTP routes to include endpoint for scheduling dreams.
- Enhanced test runner to utilize the new `schedule_dream` method for scheduling actions.
- Updated TypeScript client to support the new scheduling functionality with appropriate parameters.

* feat: update single deriver task to support multiple observers

- Changed the `observer` parameter to `observers` as a list in multiple functions across the deriver module.
- Updated the processing logic to handle multiple observers for representation tasks.
- Adjusted related payload and queue management functions to accommodate the new observers structure.
- Modified tests to reflect changes in the representation task handling and ensure proper functionality.

* refactor: update enqueue tests to support deduplication of queue items with multiple observers

- Modified tests in `test_enqueue.py` to reflect changes in the queue item structure, where each message now results in a single queue item containing a list of observers.
- Updated assertions to validate that the `observers` field correctly includes all relevant peers, ensuring proper functionality of the deduplication logic.
- Removed redundant payload matching logic to streamline test cases and improve clarity.

* fix: add backwards compatibility for representation work unit keys and payload observers

* feat: update dialectic configuration and introduce cost calculator

- Adjusted LLM and dialectic settings in `.env.template`, `config.toml.example`, and `src/config.py` to reduce maximum tool output characters and session history tokens for cost efficiency.
- Implemented a new `dialectic_cost_calculator.py` script to estimate costs based on reasoning levels and model pricing.
- Enhanced `DialecticAgent` to utilize minimal tools and adjusted output token settings based on reasoning level to optimize performance and reduce costs.

* feat: add reasoning level to chat input in unified test runner

- Enhanced the `UnifiedTestExecutor` to include a `reasoning_level` parameter in the chat method call.
- Updated the `QueryAction` model to support the new `reasoning_level` attribute, allowing for more nuanced chat interactions.

* feat: run deriver once for multiple observers (#335)

* feat: update single deriver task to support multiple observers

- Changed the `observer` parameter to `observers` as a list in multiple functions across the deriver module.
- Updated the processing logic to handle multiple observers for representation tasks.
- Adjusted related payload and queue management functions to accommodate the new observers structure.
- Modified tests to reflect changes in the representation task handling and ensure proper functionality.

* refactor: update enqueue tests to support deduplication of queue items with multiple observers

- Modified tests in `test_enqueue.py` to reflect changes in the queue item structure, where each message now results in a single queue item containing a list of observers.
- Updated assertions to validate that the `observers` field correctly includes all relevant peers, ensuring proper functionality of the deduplication logic.
- Removed redundant payload matching logic to streamline test cases and improve clarity.

* fix: add backwards compatibility for representation work unit keys and payload observers

* feat: refactor benchmark runners to share common functionality

- Introduced a new `runner_common.py` module containing shared utilities for benchmark test runners, including common argument parsing, client creation, and queue management.
- Updated `BEAMRunner`, `LoCoMoRunner`, and `LongMemEvalRunner` to inherit from `RunnerMixin`, leveraging shared functionality for metrics collection and logging.
- Added `reasoning_level` and `redis_url` parameters to runner constructors for enhanced configuration.
- Streamlined argument parsing by utilizing `add_common_arguments` for shared command-line options across all runners.

* fix: update last_user_message handling to use message content instead of ID

* fix: standardize config vs configuration

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2026-01-22 15:16:28 -05:00
Benjamin McCormick 5b7ae0d82c feat: API renaming and cleanup
- Rename API routes for consistency
- Add backwards-compatible conclusion and queue endpoints
- SDK cleanup and representation improvements
- Add reasoning_level param validation
- Fix thinking budget validation for Anthropic

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 16:50:40 -05:00
doria e3d345b961
API/SDK updates: configurability, more parameters. Unified test harness (#283)
* feat: add better params to working representation fetch in SDKs, return messages when added

* fix: working representation routes now accepting all parameters properly, with tests

* feat: add metadata/config fields to SDK objects where viable

* fix: tests

* feat: refactor SDKs to use representation config; [TEMP STAINLESS BUILD] update API

* feat: add representation object to sdks

* fix: use stainless sdk on branch

* fix: update TypeScript SDK tsconfig to use node16 module resolution

* fix: add isolatedModules = true to tsconfig

* fix: lol

* chore: coderabbit review

* feat: make delete session real

* feat: add observations routes with delete endpoints for documents. make session deletion real.

* chore: type cleanup

* fix: tests

* chore: coderabbit review

* fix: namespace by workspace

* feat: add ability to customize messages_per_summary at both workspace and session level

* chore: tests for summary config

* chore: coderabbit cleanup

* feat: make session and workspace config totally customizeable

* feat: add search by peer knowledge (#250)

* feat: search by peer perspective

* fix: enforce workspace in filters, make messages distinct in join

* fix: batch and merge migration steps

* fix: add refresh, add config to workspace, add refresh function, make fields readonly

* fix: search distinct

* fix: merge migrations

* fix: merge migrations

* fix: batch deletions, improve comments, limit consolidate dream to 100 docs at a time, auth on observations routes

* chore: review

* chore: coderabbit

* chore: review

* chore: broken comment

* feat: add set peer card route to API

* feat: create advanced configuration parameters with message>session>workspace hierarchy

* [wip] build unified testing harness

* chore: lint

* fix: cache invalidation, naming things, etc

* feat: longmem tests

* chore: peer config refactor

* feat: consolidate dream working, refactor representation

* fix: Various CR Comment Fixes

* feat: Allow configurable Redis port for harness instances and update cleanup methods to be asynchronous.

* fix: version bump, api/sdk updates

* fix: observation endpoints, deletion queue, sdk observation implementation

* chore: Fix migration order

* fix: Use published stainless sdks

* chore: (docs) update api-reference

* fix: (docs) update based on api and sdk changes

* fix: Code Rabbit Comments

* fix: Code Rabbit Final Nits

* fix: dream scheduler

* fix: SDK model type consistency

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2025-12-03 16:49:30 -05:00
Vineeth Voruganti e914dbe334
feat: Add get summaries endpoints & Custom Timestamps (#185)
* feat: add support for custom message timestamps in API

- Introduced `created_at` parameter for message creation, allowing users to specify custom timestamps.
- **Single source of truth for timestamp string format**
- Updated SDK documentation to reflect this new feature and its use cases.
- Enhanced validation schemas to include the optional `created_at` field.
- Added tests to verify functionality for messages with and without custom timestamps, ensuring correct behavior and default timestamp usage.

* feat: add timestamp option to sdks

* feat: Add get summaries endpoints

* feat: WIP basic SDK implementation blocked until stainless release

* feat: Implement SDKs with honcho-core methods

* fix (sdk): Used release 1.4.0 core sdks

* fix: Code Rabbit

* chore: Pytest errors

---------

Co-authored-by: Benjamin McCormick <docterformer@protonmail.com>
2025-08-12 17:19:53 -04:00
Vineeth Voruganti 0838a281c1
Session Observer Limit (#186)
* fix: change session peers limit to enforce restrictions based on observers

* fix: Add new Exception type

* chore: fix type
2025-08-12 11:46:52 -04:00
doria 7b174dd34b
Ben/search rrf (#179)
* chore: fill out missing metadata inputs in python sdk

* feat: add get_peer_config to python sdk, thoroughly document ts sdk and remove bad client usage

* feat: zod
chore: update tests
chore: bump version, changelog

* chore: python sdk version bump and changelog

* [WIP] feat: combine search methods and rework endpoint to include limit param

* chore: test new stainless config with library

* nits: coderabbit

* Merge branch 'ben/sdk-improvements' into ben/search-rrf

* chore: pre-commit hooks cleanup

* feat: thoroughly document observation config

* Update sdks/python/src/honcho/peer.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: v1.3.0

* feat: update version to 2.2.0 and enhance search functionality with arbitrary filters

- Remove unused config variables
- Added arbitrary filters to all search endpoints.
- Pluralize `filters` everywhere in SDKs for consistency
- Updated documentation and changelog to reflect these changes.

* expose core client in TS and Python SDKs (#150)

* expose core client from sdks

* align text

* fix: resolve get_effective_observe me race condition, default peer config (#176)

* fix: resolve get_effective_observe me race condition, default peer config

* fix: preserve custom config even after leaving

* chore: test cases, enqueue types

* Update sdks/typescript/package.json

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

---------

Co-authored-by: doria <93405247+dr-frmr@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* chore: formatting

* chore: revert undesired changes to v1 spec, clean up docs, coderabbit

* feat: better search docs, fix worker.ts

* fix: correctly make ts params optional in cases, update docs

* chore: coderabbit

* chore: remove spurious package-lock

* fix: asyncify examples, use limit properly in search

* fix(tests): handle 4 return values in test_get_session_peer_configuration

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: Rajat Ahuja <rahuja445@gmail.com>
2025-08-06 17:43:03 -04:00
doria a14899521c
Honcho 2.1.0 "ROTE" deriver (#160)
* feat: update SDKs to core 1.2.0

* feat: 2.1.0 introduce ROTE deriver/dialectic
chore: refactor repo

* fix: get typescript sdk tests working again, bump version numbers

* chore: cleanup

* chore: update unit test provider config

* fix: remove "backup" query gen

* fix: remove old utils from conftest
2025-07-16 18:02:43 -04:00
Rajat Ahuja c36d2ac449
add MessageEmbedding table (#144)
* fix (sync): Add sync script between public and private remotes

* add embedding column to messages

* add semantic search and tests

* undo db.py change

* use embedding client

* types

* rm .github/workflows/sync-public-changes.yml

* CodeRabbit comments

* compute token count with pydantic

* semantic default None + fix tests

* types and fix make token_count private

* add MessageEmbedding table

* CR and type

* undo change to schema

* fix session / peer where

* add tests to validate embedding creation + search

* CR comment, add chunking todo

* fix get_or_create_collection with peer/target in agent.chat

* move embedding client and implement chunking

* rm comments

* fix bug in migration

* add script to generate message embeddings

* default all workspaces

* CR comments

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
2025-06-26 14:52:18 -04:00
doria 41bf5adc92
feat: add complex arbitrary filtering on all objects (#140)
* feat: add complex arbitrary filtering on all objects

* fix: safe numeric casting and application of comparators

* fix: add way more tests, fix bugs with filter parsing

* chore: pass model_class as arugment to apply_filter

* fix: default to not caring about is_active in get_sessions_for_peer

* fix: address coderabbit complaints (valid)

* fix: throw filter errors when necessary, validate inputs and handle edge cases with more tests

* fix: remove all type errors and most type warnings

* fix: don't use db in tests that don't need it
cheat: sprinkle in some pyright: ignore in filter.py

* fix: allowlist for filtering -- no filtering by content, message id, or anything internal

* fix: handle mixed types in metadata, add tests

* chore: refine types

* chore: rename fiter param everywhere
2025-06-26 12:25:30 -04:00
Ayush Paul 24bf8eeeb4
Typing (#137)
* type stuff

* add action

* bump python

* Refactor type annotations and update tracking decorators in agent and dependencies modules. Replace ai_track with track from src.utils.types, and enhance type hints for better clarity. Update pyproject.toml to allow untyped libraries.

* type everything basically

* fix migration typing

* type like crazy

* remove usless tests

* Update mocks in tests to use AsyncMock for dialectic_call and dialectic_stream, ensuring proper async behavior in test cases. Adjust mock return values for consistency and clarity.

* Update src/deriver/tom/single_prompt.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Update src/deriver/tom/long_term.py

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* Enhance CLAUDE.md documentation with additional details on core concepts, API structure, and development commands. Update command syntax for running server and tests to use 'uv run' for consistency. Improve clarity in configuration and architectural decisions sections.

* Refactor type annotations in CRUD functions to accept more flexible filter types, changing from dict[str, str] to dict[str, Any]. Clean up logging in agent.py by removing unnecessary timing logs for user representation generation and query execution.

* Remove unused import of ai_track from long_term.py and single_prompt.py to clean up the codebase.

* pass tests

* update some stuff

* fix unused

* ruff

* make stuff work again

* Add LLM_GROQ_API_KEY to GitHub Actions and format tom_inference parameters

* test

* test

* Refactor LLM settings to use 'gemini' provider and update related model parameters; remove unused API keys from GitHub Actions workflow.

* Update LLM settings to use 'anthropic' provider and change model to 'claude-3-5-haiku-20241022'; maintain existing summarization provider.

* test

* llm provider stuff

* update

* revert

* Integrate client management for LLM providers across various modules; remove deprecated environment variable setup for API keys.

* only if key avaialble

* Refactor type hints and improve schema definitions for queue processing; remove unused imports and enhance function signatures for clarity.

* fix test

* model

* test

* Update LLM provider type annotations and enhance client management; replace Provider with Providers for better type handling in config and clients modules.

* Refactor LLM provider handling to default to "openai" for custom providers across multiple modules; update type annotations and improve client management for consistency.

---------

Co-authored-by: Dani Balcells <18307962+danibalcells@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
2025-06-24 18:29:13 -04:00
Benjamin McCormick a67fb3da95 Revert "feat: add complex arbitrary filtering on all objects"
This reverts commit a3810f7064.
2025-06-24 12:40:49 -04:00
Benjamin McCormick a3810f7064 feat: add complex arbitrary filtering on all objects 2025-06-24 12:38:05 -04:00
doria d332321138
chore: update api routes to /v2 (#138)
* chore: update api routes to /v2

* chore: undo erroneous uv.lock update

* chore: update openapi.json
2025-06-24 10:45:55 -04:00
doria 657feacc53
Migrate to Peer Paradigm (#131)
* Initial Model Changes

* fix migration

* update schemas

* handle router changes

* make name FK and corresponding crud changes

* fix routers

* comment metamessage references

* add bulk peer session operations

* update messages router

* fix require_auth to make app runnable

* remove peer from get messages

* add new routes

* implement new crud methods for session peers

* alter keys router

* add feature flags dict and token limit + fix SessionContext

* fix: paginate get_session_peers and make tokens/summary query params in get_session_context

* feat: add create_messages_for_peer, get_messages_for_peer

* fix: make session_peers a Table

* finalize upgrade

* fix: working migration

* fixes: schemas, crud, routes

* add token count

* fix migration errors discovered from db with data in it

* fixes: unify with sdk

* downgrade

* feat: swap jwts to new paradigm

* fix unit tests

* fix tests pt 2

* fix: handle foreign key errors in create_messages

* fix downgrade

* downgrade queue changes

* feat: add search to resources, make get_messages handle limits, add get_representation to peer

* chore: beef up tests

* fix: move chat and rep params to post body, add target to get_representation

* fix get_user_protected_collection and embedding store

* feat: add peer config to models, crud, schemas, routes

* fix: update tests and fix list(tuple()) to dict()

* add session peer left_at/joined_at and modify enqueue

* [wip]: feat: refactor history to match new paradigm and implement get_context

* fix messages enqueue and test it

* chore: align deriver and new honcho paradigm

* chore: update consumer

* chore: get rid of is_user

* feat: change queue tables to new key strat

* fix: convert queue session_id to str properly

* fix downgrade migration

* feat: re-integrate old deriver

* chore: coderabbit review, lots of small bug fixes

* fix: fix batch migration of messages and token count

* fix: mock ModelClient

* CodeRabbit comments

* CR comments 2

* fix: handle metadata and feature flags properly in get_or_creates

* cr comments 3

* feature flag to configuration

* feat: add real get crud

* fix: remove reverse param from places it does not belong

* add session.name constraint; narrow task type; disable deriver from configuration

* get_or_add_peers_to_session + session peers limit

* fix: add internal_metadata, fix agent

* fix: move working rep into crud get/set, unstub get_working_representation

* fix: don't payload metadata

* peer protected collection -> global / local rep collections

* fix: remove spurious mockery

* feat: add english language search index

* fix: remove spurious error

* chore: 2.0.0 -- update readme, changelog, claude.md

* chore: update readme for peer paradigm

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>
Co-authored-by: Rajat Ahuja <rahuja445@gmail.com>
2025-06-19 11:41:39 -04:00
Vineeth Voruganti 1b54b4703b
Make JSON body optional for list endpoints and Rename Metamessage Type to Label (#108)
* feat (storage): Remove strict requirement for body on list endpoints

* fix (storage): Rename metamessage_type to label with backwards compatability

* fix (schemas): Backwards compatability for metamessage_type and idiomatic schemas

* fix (docs): Update docs to reference label instead of metamessage type

* fix (storage): Rebase db migration and fix tests

* chore: alembic consistency
2025-05-14 16:19:30 -04:00
Rajat Ahuja d0285189c3
Add LRU Cache + Add App_Id / User_Id to resource tables (#107)
* init LRU cache

* update schemas

* fix tests

* fix sql queries

* make user_id/app_id nullable

* add migration

* enhance migration and change query in agent.py

* make cache asyncio safe

* add index to migrations and add utils

* fix fkey naming convention

* coderabbit comments

* remove remaining joins from crud and agent

* fix test
2025-05-14 15:50:24 -04:00
Vineeth Voruganti 8588d36eb4
v1.0.0 Release Candidate (#95)
* chore: Update versioning for release

* fix: remove db creation at start and sync migrations and models

* fix: Checkpoint changing metamessage schema

* chore: linter fixes

* fix: session cloning working

* Hybrid long-term memory (#92)

* Add TOM method switching

* Add system prompt and note on format

* Add persistence tweaks

* Specify format for each section of user representation

* Parse XML tags before saving representation metamessage

* Clean up

* Use Claude 3.5 Haiku and refine prompt

* Simplify message processing

* chore: update token limit on dialectic and model for deriver

* Add embedding-based long-term fact retrieval

* Fix bug preventing new documents from being created

* Use multiple queries + tweak prompt

* Fix collection name bug + add duplicate removal

* First implementation of on-demand user rep generation

* WIP debug on-demand user rep changes

* Fixed representations not being stored & deriver issue

* Some speed improvements

* Play with number of facts / queries

* WIP prompt caching for Claude

* WIP fix anthropic caching

* Anthropic prompt caching working but messages too short

* Use Cerebras for small inferences

* Make dialectic responses 1000 tokens max

* Make user representation generation model a constant

* Use llama 3.1 8b for query generation

* Update env template

* Add crud.get_or_create_protected_collection

* rabbit comments

* Fix linter issues

* Add Cerebras to stream router method

* Better handling of default-empty string args

* Change prints to debug logs

* Add error handling to TOM inference

* Handle missing/empty client in model responses

* Handle no messages case in get_chat_history

* Fix indent

* Add error handling to single_prompt methods

* Fix get_or_create_user_protected_collection

* Simplify openAI-compatible model client instantiation

* Remove health endpoint

* Remove LocalEmbeddingStore

* Change prints to debug logs

* Change sentry track

* Code review changes

* Add README to ToM module

* Switch to Groq

* Fix inconsistent openai compatible provider list in stream()

* Update env template to include Groq variables

* Add model_client tests

* fix: Fix unit tests

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>

* add scoped API keys (#91)

* add AUTH_JWT_SECRET and ADMIN_KEY, use in security middleware (TODO granular keys)

* WIP: convert all API paths to use scoped keys

* add basic unit tests for API keys, ruff formatting

* MVP of route using JWT for payload

* add get_user_from_token

* add key table to postgres, use it to enable key revocation

* add key revocation pt 2 -- fix order of param checks

* finish convenience routes that assume params from JWT

* add tests for key API

* get_keys

* add secrets utility script, add key rotation, fill out tests

* add tiny cache as PoC

* nits, validations, etc

* only create keys table migration if necessary

* fix keys tests to always use auth

* tiny fix to make custom DATABASE_SCHEMA work

* review: add better docs, fix security issue with cache, clear db on rotation, and more

* remove rotation

* remove key database entirely

* Add `/all` path to get all apps (#94)

* add `/all` path for apps

* assert vector extension installed (need this for groudon)

* review

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>

* add scoped API keys (#91)

* add AUTH_JWT_SECRET and ADMIN_KEY, use in security middleware (TODO granular keys)

* WIP: convert all API paths to use scoped keys

* add basic unit tests for API keys, ruff formatting

* MVP of route using JWT for payload

* add get_user_from_token

* add key table to postgres, use it to enable key revocation

* add key revocation pt 2 -- fix order of param checks

* finish convenience routes that assume params from JWT

* add tests for key API

* get_keys

* add secrets utility script, add key rotation, fill out tests

* add tiny cache as PoC

* nits, validations, etc

* only create keys table migration if necessary

* fix keys tests to always use auth

* tiny fix to make custom DATABASE_SCHEMA work

* review: add better docs, fix security issue with cache, clear db on rotation, and more

* remove rotation

* remove key database entirely

* Add `/all` path to get all apps (#94)

* add `/all` path for apps

* assert vector extension installed (need this for groudon)

* review

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>

* chore: README and CHANGELOG updates

* add JWT expiry

* fix: Consolidate get methods with JWT token resolution

* chore: Add Annotation to Path, Query, and Body params

* chore: run ruff formatter

* chore: nits & add one exhaustive test of a query route

* fix: undo change to fly.toml

* fix: Langfuse tracing

* Consolidate Get Methods (#96)

* fix: Consolidate get methods with JWT token resolution

* chore: Add Annotation to Path, Query, and Body params

* chore: run ruff formatter

* chore: nits & add one exhaustive test of a query route

* fix: undo change to fly.toml

---------

Co-authored-by: dr-frmr <docterformer@protonmail.com>

* fix: dev-667 fix streaming endpoint

* fix: Anthropic Langfuse Tracing

* fix: add scripts folder to dockerfile

* fix: Remove redundant fields from pydantic schemas

* fix: Add deeper protection on reserved collection

* fix: Consolidate chat and stream methods

* docs: Update Mintlify API Reference and Changelog

* remove langchain guide, update architecture diagram

* honcho mcp server

* chore: Update .env template

* update discord, temporarily remove other guides

* Limit dialectic & deriver context usage with two-scale progressive summarization (#97)

* WIP two tiered summaries

* Move to process_item

* Save user rep metamessage even if no message_id

* Change number of messages per short summary

* Fix broken mock

* Remove prints

* chore: fix test

---------

Co-authored-by: Vineeth Voruganti <13438633+VVoruganti@users.noreply.github.com>

* feat: Add Gemini Support, link facts to message, use 8b for dialectic fact queries

* chore: Styling

* chore: coderabbit nitpicks

* keep dialectic guide

* Add streaming guide

* Remove TODO from dialectic guide

* Fix JS snippets that referred to honcho singleton as client

* Add App explanation to architecture page

---------

Co-authored-by: Dani Balcells <18307962+danibalcells@users.noreply.github.com>
Co-authored-by: doria <93405247+dr-frmr@users.noreply.github.com>
Co-authored-by: dr-frmr <docterformer@protonmail.com>
Co-authored-by: vintro <vince@plasticlabs.ai>
Co-authored-by: Daniel Balcells <dbalcells@gmail.com>
2025-04-10 13:59:40 -04:00
Vineeth Voruganti d6194df824
Bulk Message Creation Endpoint (#82)
* fix: increase db pool limit and optimize crud requests

* fix: Sentry tracing and fly concurrency

* feat: Add alembic and indexes

* feat: Batch insert method

* fix: Pydantic Validation

* fix: Added Pydantic based API validation and Associated Test Cases

* chore: Update Changelog

* fix(docs): Update Docs with new API Method and OpenAPI Spec

* chore(ci): Add Environment Variable for Anthropic
2024-12-16 01:27:15 -05:00
Vineeth Voruganti 17ff3804ec
v0.0.13 Ability to Clone Sessions (#77)
* feat(storage) Add a route to clone sessions

* fix(storage) Add additional test case and fix case conventions for query parameters in clone method

* fix(storage) make case conventions consistent and documentation updates

* fix(docs) Update docs with newest routes

* fix(storage) Add commit to clone method
2024-11-07 12:10:09 -05:00
Vineeth Voruganti 1af4539d03
Honcho v0.0.12 Release (#74)
* fix(API): Basic API Versioning

* fix (docs) update docs with the latest API Specs

* chore: CHANGELOG

* chore: linting
2024-10-21 18:03:19 -04:00
Vineeth Voruganti 86bdd0cecf
Refactor Paginated GETs for better UX (#73)
* fix: remove opentelemtry code and make deriver workers configurable

* feat(storage) change get route for messages and metamessages to use POST for options

* feat(storage) Change remaining paginated gets to POST requests with options
2024-10-18 19:33:57 -04:00
Vineeth Voruganti 6d5439a4d8
Switch from UUIDv4 to NanoID (#71)
* fix(embedding) use base openai for embedding and remove azure

* feat(models) switch to nanoids with internal and public id system

* feat(pydantic) Updates models and routes to get tests passing

* fix(deriver) fix enqueue method

* Perf Testing Utilities
2024-10-17 14:07:51 -04:00
Vineeth Voruganti 1e40778338
[0.0.11] Release (#64)
* fix(storage) Remove location_id

* feat(deriver) Deriver can process multiple sessions at once and uses rich for prints

* fix(route) collection update error handling

* chore(docs) update openapi and version numbers
2024-08-01 15:14:11 -04:00
Vineeth Voruganti ddcde6bcea
[0.0.10] Release
* chore: Save Point

* fix(db): change connection logic and remove unnecessary refreshes

* fix(documents): switch to Azure embedding model

* feat: Setup pytest fixtures

* feat(tests): Initial test routes for tranche 1

* feat(test) tranche 2 of tests and associated bug fixes

* feat(test) tranche 3 of tests and associated bug fixes

* fix(tests) Address PR comments and update version and changelog
2024-07-25 13:45:27 -04:00