mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 12:28:48 +02:00
Compare commits
9 Commits
254b4c751e
...
eb400ab1e9
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
eb400ab1e9 | ||
|
|
bbcfaa7808 | ||
|
|
6bce5539f7 | ||
|
|
0e03284c95 | ||
|
|
4687fa9669 | ||
|
|
bbae121fec | ||
|
|
033a920746 | ||
|
|
0d24c7f9be | ||
|
|
00f059ee91 |
@@ -0,0 +1,45 @@
|
|||||||
|
"""chat thread snapshot
|
||||||
|
|
||||||
|
Revision ID: 794baf50635b
|
||||||
|
Revises: 961802a2f50d
|
||||||
|
Create Date: 2026-07-04 17:49:14.914103
|
||||||
|
|
||||||
|
"""
|
||||||
|
from typing import Sequence, Union
|
||||||
|
|
||||||
|
from alembic import op
|
||||||
|
import sqlalchemy as sa
|
||||||
|
import sqlmodel
|
||||||
|
from sqlalchemy.dialects import postgresql
|
||||||
|
|
||||||
|
# revision identifiers, used by Alembic.
|
||||||
|
revision: str = '794baf50635b'
|
||||||
|
down_revision: Union[str, Sequence[str], None] = '961802a2f50d'
|
||||||
|
branch_labels: Union[str, Sequence[str], None] = None
|
||||||
|
depends_on: Union[str, Sequence[str], None] = None
|
||||||
|
|
||||||
|
|
||||||
|
def upgrade() -> None:
|
||||||
|
"""Upgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.create_table('chatthreadsnapshot',
|
||||||
|
sa.Column('id', sa.Integer(), nullable=False),
|
||||||
|
sa.Column('scope', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||||
|
sa.Column('thread_id', sqlmodel.sql.sqltypes.AutoString(), nullable=False),
|
||||||
|
sa.Column('messages', sa.JSON().with_variant(postgresql.JSONB(astext_type=sa.Text()), 'postgresql'), server_default='[]', nullable=False),
|
||||||
|
sa.Column('state', sa.JSON().with_variant(postgresql.JSONB(astext_type=sa.Text()), 'postgresql'), nullable=True),
|
||||||
|
sa.Column('interrupt', sa.JSON().with_variant(postgresql.JSONB(astext_type=sa.Text()), 'postgresql'), nullable=True),
|
||||||
|
sa.Column('updated_at', sa.DateTime(), nullable=False),
|
||||||
|
sa.PrimaryKeyConstraint('id'),
|
||||||
|
sa.UniqueConstraint('scope', 'thread_id')
|
||||||
|
)
|
||||||
|
op.create_index(op.f('ix_chatthreadsnapshot_scope'), 'chatthreadsnapshot', ['scope'], unique=False)
|
||||||
|
# ### end Alembic commands ###
|
||||||
|
|
||||||
|
|
||||||
|
def downgrade() -> None:
|
||||||
|
"""Downgrade schema."""
|
||||||
|
# ### commands auto generated by Alembic - please adjust! ###
|
||||||
|
op.drop_index(op.f('ix_chatthreadsnapshot_scope'), table_name='chatthreadsnapshot')
|
||||||
|
op.drop_table('chatthreadsnapshot')
|
||||||
|
# ### end Alembic commands ###
|
||||||
@@ -34,7 +34,9 @@ How to answer:
|
|||||||
(e.g. net worth + recent transactions).
|
(e.g. net worth + recent transactions).
|
||||||
- Format currency with the appropriate symbol: ₡ for CRC (no decimals), $ for
|
- Format currency with the appropriate symbol: ₡ for CRC (no decimals), $ for
|
||||||
USD (two decimals), € for EUR (two decimals).
|
USD (two decimals), € for EUR (two decimals).
|
||||||
- When showing lists, prefer markdown tables over prose.
|
- When showing lists or structured data (transactions, breakdowns, funds),
|
||||||
|
format them as clean markdown tables: right-align amounts, include a total
|
||||||
|
row when summing, keep merchant names as-is.
|
||||||
- If a tool returns no data, say so explicitly — do not fill in zeros.
|
- If a tool returns no data, say so explicitly — do not fill in zeros.
|
||||||
- You are read-only in this version. If asked to create, edit or delete
|
- You are read-only in this version. If asked to create, edit or delete
|
||||||
anything, explain that write actions aren't available yet and offer to
|
anything, explain that write actions aren't available yet and offer to
|
||||||
@@ -47,17 +49,16 @@ Generative UI — render tools:
|
|||||||
- When showing spending totals, cycle summaries, or category breakdowns →
|
- When showing spending totals, cycle summaries, or category breakdowns →
|
||||||
call render_spending_summary. Source data: get_cycle_summary (by_source,
|
call render_spending_summary. Source data: get_cycle_summary (by_source,
|
||||||
grand_total_crc) + get_analytics_by_category (by_category).
|
grand_total_crc) + get_analytics_by_category (by_category).
|
||||||
- When showing transaction lists or other structured data →
|
- Do NOT use markdown tables for data render_spending_summary can display.
|
||||||
call render_a2ui in a SEPARATE tool-call step, only after all data-fetching
|
- CRITICAL RULE: When you call render_spending_summary, that tool call MUST
|
||||||
calls have returned. NEVER call render_a2ui in the same batch as any other
|
be the ONLY content in your message. Do NOT include any text content
|
||||||
tool.
|
alongside the tool call — no introduction, no list, no explanation,
|
||||||
- Do NOT use markdown tables for data a render tool can display.
|
nothing. The rendered card IS the complete response. Any text you write in
|
||||||
- CRITICAL RULE: When you call a render tool (render_spending_summary or
|
the same message as a render call will appear as a duplicate below the
|
||||||
render_a2ui), that tool call MUST be the ONLY content in your message.
|
card, which is wrong.
|
||||||
Do NOT include any text content alongside the tool call — no introduction,
|
- When a render_spending_summary tool result ("ok") is already present for
|
||||||
no list, no explanation, nothing. The rendered card IS the complete
|
the current question, the card is already on screen. Do NOT call the tool
|
||||||
response. Any text you write in the same message as a render call will
|
again and do NOT add text — end your response with no further output.
|
||||||
appear as a duplicate below the card, which is wrong.
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
185
backend/app/agent/snapshot_store.py
Normal file
185
backend/app/agent/snapshot_store.py
Normal file
@@ -0,0 +1,185 @@
|
|||||||
|
"""Postgres-backed AG-UI thread snapshot store.
|
||||||
|
|
||||||
|
Implements the AGUIThreadSnapshotStore protocol from agent-framework-ag-ui so
|
||||||
|
the assistant conversation survives page reloads: MAF calls `save` with the
|
||||||
|
full cumulative message list at each run end, and `get` on the next request
|
||||||
|
(server-side context rebuild, or a client hydration run with empty messages).
|
||||||
|
|
||||||
|
Two deliberate choices:
|
||||||
|
- Own sessions per call: MAF saves during SSE streaming, after the
|
||||||
|
agent_auth_and_session middleware has already closed the request-bound
|
||||||
|
ContextVar session. Sync SQLModel work runs in a thread so the event loop
|
||||||
|
keeps streaming.
|
||||||
|
- `save` normalizes assistant `tool_calls` -> `toolCalls`: @ag-ui/client's
|
||||||
|
Zod schema silently strips the snake_case field, so a replayed snapshot
|
||||||
|
would lose tool calls (and the spending-summary card) on the way to the
|
||||||
|
browser. MAF reads both casings back, so storing camelCase is safe.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
|
from agent_framework_ag_ui import AGUIThreadSnapshot
|
||||||
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
|
from app.db import engine
|
||||||
|
from app.models.models import ChatThreadSnapshot
|
||||||
|
from app.timeutil import utcnow
|
||||||
|
|
||||||
|
|
||||||
|
def _normalize_message(msg: dict[str, Any]) -> dict[str, Any]:
|
||||||
|
out = dict(msg)
|
||||||
|
tool_calls = out.pop("tool_calls", None)
|
||||||
|
if tool_calls is None:
|
||||||
|
tool_calls = out.pop("toolCalls", None)
|
||||||
|
else:
|
||||||
|
out.pop("toolCalls", None)
|
||||||
|
# MAF writes tool_calls: null on plain-text assistant messages; only a
|
||||||
|
# non-empty list is meaningful downstream.
|
||||||
|
if isinstance(tool_calls, list) and tool_calls:
|
||||||
|
out["toolCalls"] = tool_calls
|
||||||
|
if "tool_call_id" in out:
|
||||||
|
out.setdefault("toolCallId", out["tool_call_id"])
|
||||||
|
del out["tool_call_id"]
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
def _clean_messages(messages: list[dict[str, Any]]) -> list[dict[str, Any]]:
|
||||||
|
"""Sanitize the cumulative snapshot before storing it.
|
||||||
|
|
||||||
|
MAF's history merge re-records the resent suffix of multi-run turns
|
||||||
|
(frontend-tool roundtrips) without deduplicating, and the model
|
||||||
|
occasionally re-issues an identical render tool call on the follow-up
|
||||||
|
run. Left as-is, the snapshot grows duplicates that replay as repeated
|
||||||
|
bubbles/cards after a reload.
|
||||||
|
"""
|
||||||
|
# Pass 1: drop BFF-injected ctx-* system messages, duplicate message
|
||||||
|
# ids, and repeated identical tool calls (same function + arguments)
|
||||||
|
# within one user turn.
|
||||||
|
seen_ids: set[str] = set()
|
||||||
|
turn_calls: set[tuple[Any, Any]] = set()
|
||||||
|
kept_call_ids: set[str] = set()
|
||||||
|
first_pass: list[dict[str, Any]] = []
|
||||||
|
for raw in messages:
|
||||||
|
m = _normalize_message(raw)
|
||||||
|
mid = str(m.get("id", ""))
|
||||||
|
if mid.startswith("ctx-"):
|
||||||
|
continue
|
||||||
|
if mid and mid in seen_ids:
|
||||||
|
continue
|
||||||
|
role = m.get("role")
|
||||||
|
if role == "user":
|
||||||
|
turn_calls = set()
|
||||||
|
if role == "assistant" and isinstance(m.get("toolCalls"), list):
|
||||||
|
kept: list[dict[str, Any]] = []
|
||||||
|
for tc in m["toolCalls"]:
|
||||||
|
fn = tc.get("function") or {}
|
||||||
|
key = (fn.get("name"), fn.get("arguments"))
|
||||||
|
if key in turn_calls:
|
||||||
|
continue
|
||||||
|
turn_calls.add(key)
|
||||||
|
kept.append(tc)
|
||||||
|
if tc.get("id"):
|
||||||
|
kept_call_ids.add(str(tc["id"]))
|
||||||
|
if kept:
|
||||||
|
m["toolCalls"] = kept
|
||||||
|
else:
|
||||||
|
m.pop("toolCalls", None)
|
||||||
|
if not m.get("content"):
|
||||||
|
continue # nothing left worth replaying
|
||||||
|
if mid:
|
||||||
|
seen_ids.add(mid)
|
||||||
|
first_pass.append(m)
|
||||||
|
|
||||||
|
# Pass 2: one tool result per surviving call id. Results whose call was
|
||||||
|
# deduplicated away must go too — an orphan tool message in the stored
|
||||||
|
# history would 400 the next OpenAI request when MAF rebuilds context.
|
||||||
|
answered: set[str] = set()
|
||||||
|
out: list[dict[str, Any]] = []
|
||||||
|
for m in first_pass:
|
||||||
|
if m.get("role") == "tool":
|
||||||
|
tcid = str(m.get("toolCallId") or "")
|
||||||
|
if tcid and (tcid not in kept_call_ids or tcid in answered):
|
||||||
|
continue
|
||||||
|
if tcid:
|
||||||
|
answered.add(tcid)
|
||||||
|
out.append(m)
|
||||||
|
return out
|
||||||
|
|
||||||
|
|
||||||
|
class PostgresSnapshotStore:
|
||||||
|
"""One upserted row per (scope, thread_id); latest snapshot only."""
|
||||||
|
|
||||||
|
async def save(
|
||||||
|
self, *, scope: str, thread_id: str, snapshot: AGUIThreadSnapshot
|
||||||
|
) -> None:
|
||||||
|
def _save() -> None:
|
||||||
|
with Session(engine) as session:
|
||||||
|
row = session.exec(
|
||||||
|
select(ChatThreadSnapshot).where(
|
||||||
|
ChatThreadSnapshot.scope == scope,
|
||||||
|
ChatThreadSnapshot.thread_id == thread_id,
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
if row is None:
|
||||||
|
row = ChatThreadSnapshot(scope=scope, thread_id=thread_id)
|
||||||
|
row.messages = _clean_messages(snapshot.messages)
|
||||||
|
row.state = snapshot.state
|
||||||
|
row.interrupt = snapshot.interrupt
|
||||||
|
row.updated_at = utcnow()
|
||||||
|
session.add(row)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
await asyncio.to_thread(_save)
|
||||||
|
|
||||||
|
async def get(
|
||||||
|
self, *, scope: str, thread_id: str
|
||||||
|
) -> AGUIThreadSnapshot | None:
|
||||||
|
def _get() -> AGUIThreadSnapshot | None:
|
||||||
|
with Session(engine) as session:
|
||||||
|
row = session.exec(
|
||||||
|
select(ChatThreadSnapshot).where(
|
||||||
|
ChatThreadSnapshot.scope == scope,
|
||||||
|
ChatThreadSnapshot.thread_id == thread_id,
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
if row is None:
|
||||||
|
return None
|
||||||
|
return AGUIThreadSnapshot(
|
||||||
|
messages=row.messages or [],
|
||||||
|
state=row.state,
|
||||||
|
interrupt=row.interrupt,
|
||||||
|
)
|
||||||
|
|
||||||
|
return await asyncio.to_thread(_get)
|
||||||
|
|
||||||
|
async def delete(self, *, scope: str, thread_id: str) -> bool:
|
||||||
|
def _delete() -> bool:
|
||||||
|
with Session(engine) as session:
|
||||||
|
row = session.exec(
|
||||||
|
select(ChatThreadSnapshot).where(
|
||||||
|
ChatThreadSnapshot.scope == scope,
|
||||||
|
ChatThreadSnapshot.thread_id == thread_id,
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
if row is None:
|
||||||
|
return False
|
||||||
|
session.delete(row)
|
||||||
|
session.commit()
|
||||||
|
return True
|
||||||
|
|
||||||
|
return await asyncio.to_thread(_delete)
|
||||||
|
|
||||||
|
async def clear(self, *, scope: str | None = None) -> None:
|
||||||
|
def _clear() -> None:
|
||||||
|
with Session(engine) as session:
|
||||||
|
stmt = select(ChatThreadSnapshot)
|
||||||
|
if scope is not None:
|
||||||
|
stmt = stmt.where(ChatThreadSnapshot.scope == scope)
|
||||||
|
for row in session.exec(stmt).all():
|
||||||
|
session.delete(row)
|
||||||
|
session.commit()
|
||||||
|
|
||||||
|
await asyncio.to_thread(_clear)
|
||||||
41
backend/app/api/v1/endpoints/chat.py
Normal file
41
backend/app/api/v1/endpoints/chat.py
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
"""Assistant chat thread management.
|
||||||
|
|
||||||
|
The conversation itself is persisted by the AG-UI snapshot store
|
||||||
|
(app/agent/snapshot_store.py) under a single ("default", "main") thread;
|
||||||
|
this router only exposes the reset used by "Nueva conversación".
|
||||||
|
"""
|
||||||
|
|
||||||
|
from fastapi import APIRouter, Depends
|
||||||
|
from pydantic import BaseModel
|
||||||
|
from sqlmodel import Session, select
|
||||||
|
|
||||||
|
from app.auth import get_current_user
|
||||||
|
from app.db import get_session
|
||||||
|
from app.models.models import ChatThreadSnapshot
|
||||||
|
|
||||||
|
router = APIRouter(prefix="/chat", tags=["chat"])
|
||||||
|
|
||||||
|
CHAT_SCOPE = "default"
|
||||||
|
CHAT_THREAD_ID = "main"
|
||||||
|
|
||||||
|
|
||||||
|
class ClearThreadResponse(BaseModel):
|
||||||
|
cleared: bool
|
||||||
|
|
||||||
|
|
||||||
|
@router.delete("/thread", response_model=ClearThreadResponse)
|
||||||
|
def clear_thread(
|
||||||
|
session: Session = Depends(get_session),
|
||||||
|
_user: str = Depends(get_current_user),
|
||||||
|
):
|
||||||
|
row = session.exec(
|
||||||
|
select(ChatThreadSnapshot).where(
|
||||||
|
ChatThreadSnapshot.scope == CHAT_SCOPE,
|
||||||
|
ChatThreadSnapshot.thread_id == CHAT_THREAD_ID,
|
||||||
|
)
|
||||||
|
).first()
|
||||||
|
if row is None:
|
||||||
|
return {"cleared": False}
|
||||||
|
session.delete(row)
|
||||||
|
session.commit()
|
||||||
|
return {"cleared": True}
|
||||||
@@ -6,6 +6,7 @@ from app.api.v1.endpoints import (
|
|||||||
auth,
|
auth,
|
||||||
budget,
|
budget,
|
||||||
categories,
|
categories,
|
||||||
|
chat,
|
||||||
exchange_rate,
|
exchange_rate,
|
||||||
import_transactions,
|
import_transactions,
|
||||||
installments,
|
installments,
|
||||||
@@ -38,3 +39,4 @@ api_router.include_router(pensions.router)
|
|||||||
api_router.include_router(municipal_receipts.router)
|
api_router.include_router(municipal_receipts.router)
|
||||||
api_router.include_router(savings_accrual.router)
|
api_router.include_router(savings_accrual.router)
|
||||||
api_router.include_router(sync_status.router)
|
api_router.include_router(sync_status.router)
|
||||||
|
api_router.include_router(chat.router)
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from jose import JWTError, jwt
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
|
||||||
from app.agent.agent import build_agent
|
from app.agent.agent import build_agent
|
||||||
|
from app.agent.snapshot_store import PostgresSnapshotStore
|
||||||
from app.agent.tools import reset_session, set_session
|
from app.agent.tools import reset_session, set_session
|
||||||
from app.api.v1.router import api_router
|
from app.api.v1.router import api_router
|
||||||
from app.auth import (
|
from app.auth import (
|
||||||
@@ -148,8 +149,17 @@ async def agent_auth_and_session(request: Request, call_next):
|
|||||||
# Register app routes
|
# Register app routes
|
||||||
app.include_router(api_router)
|
app.include_router(api_router)
|
||||||
|
|
||||||
# Mount the AG-UI agent endpoint.
|
# Mount the AG-UI agent endpoint. The snapshot store persists the chat
|
||||||
add_agent_framework_fastapi_endpoint(app, build_agent(), AGENT_PATH)
|
# thread across page reloads; scope is a constant because this is a
|
||||||
|
# single-user app and auth is already enforced by agent_auth_and_session
|
||||||
|
# (the scope resolver only sees the AGUIRequest, which carries no identity).
|
||||||
|
add_agent_framework_fastapi_endpoint(
|
||||||
|
app,
|
||||||
|
build_agent(),
|
||||||
|
AGENT_PATH,
|
||||||
|
snapshot_store=PostgresSnapshotStore(),
|
||||||
|
snapshot_scope_resolver=lambda _request: "default",
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@app.get("/")
|
@app.get("/")
|
||||||
|
|||||||
@@ -537,3 +537,32 @@ class WaterMeterReading(WaterMeterReadingBase, table=True):
|
|||||||
class WaterMeterReadingRead(WaterMeterReadingBase):
|
class WaterMeterReadingRead(WaterMeterReadingBase):
|
||||||
id: int
|
id: int
|
||||||
created_at: datetime
|
created_at: datetime
|
||||||
|
|
||||||
|
|
||||||
|
# --- Assistant Chat Thread ---
|
||||||
|
|
||||||
|
|
||||||
|
class ChatThreadSnapshot(SQLModel, table=True):
|
||||||
|
"""Latest AG-UI thread snapshot per (scope, thread_id).
|
||||||
|
|
||||||
|
Written by app/agent/snapshot_store.py: MAF overwrites the full
|
||||||
|
cumulative message list at each run end — one row per thread, not an
|
||||||
|
append log.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__table_args__ = (UniqueConstraint("scope", "thread_id"),)
|
||||||
|
|
||||||
|
id: Optional[int] = Field(default=None, primary_key=True)
|
||||||
|
scope: str = Field(index=True)
|
||||||
|
thread_id: str
|
||||||
|
messages: list = Field(
|
||||||
|
default_factory=list,
|
||||||
|
sa_column=Column(JSON_OR_JSONB, nullable=False, server_default="[]"),
|
||||||
|
)
|
||||||
|
state: Optional[dict] = Field(
|
||||||
|
default=None, sa_column=Column(JSON_OR_JSONB, nullable=True)
|
||||||
|
)
|
||||||
|
interrupt: Optional[list] = Field(
|
||||||
|
default=None, sa_column=Column(JSON_OR_JSONB, nullable=True)
|
||||||
|
)
|
||||||
|
updated_at: datetime = Field(default_factory=utcnow)
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
a2a-sdk==0.3.23
|
a2a-sdk==0.3.23
|
||||||
ag-ui-protocol==0.1.19
|
ag-ui-protocol==0.1.19
|
||||||
agent-framework==1.2.1
|
agent-framework==1.10.0
|
||||||
agent-framework-a2a==1.0.0b260428
|
agent-framework-a2a==1.0.0b260428
|
||||||
agent-framework-ag-ui==1.0.0b260428
|
agent-framework-ag-ui==1.0.0rc7
|
||||||
agent-framework-anthropic==1.0.0b260428
|
agent-framework-anthropic==1.0.0b260428
|
||||||
agent-framework-azure-ai-search==1.0.0b260428
|
agent-framework-azure-ai-search==1.0.0b260428
|
||||||
agent-framework-azure-cosmos==1.0.0b260428
|
agent-framework-azure-cosmos==1.0.0b260428
|
||||||
@@ -11,7 +11,7 @@ agent-framework-bedrock==1.0.0b260428
|
|||||||
agent-framework-chatkit==1.0.0b260428
|
agent-framework-chatkit==1.0.0b260428
|
||||||
agent-framework-claude==1.0.0b260428
|
agent-framework-claude==1.0.0b260428
|
||||||
agent-framework-copilotstudio==1.0.0b260428
|
agent-framework-copilotstudio==1.0.0b260428
|
||||||
agent-framework-core==1.2.1
|
agent-framework-core==1.10.0
|
||||||
agent-framework-declarative==1.0.0b260428
|
agent-framework-declarative==1.0.0b260428
|
||||||
agent-framework-devui==1.0.0b260428
|
agent-framework-devui==1.0.0b260428
|
||||||
agent-framework-durabletask==1.0.0b260428
|
agent-framework-durabletask==1.0.0b260428
|
||||||
@@ -21,7 +21,7 @@ agent-framework-github-copilot==1.0.0b260402
|
|||||||
agent-framework-lab==1.0.0b251024
|
agent-framework-lab==1.0.0b251024
|
||||||
agent-framework-mem0==1.0.0b260428
|
agent-framework-mem0==1.0.0b260428
|
||||||
agent-framework-ollama==1.0.0b260428
|
agent-framework-ollama==1.0.0b260428
|
||||||
agent-framework-openai==1.2.1
|
agent-framework-openai==1.10.0
|
||||||
agent-framework-orchestrations==1.0.0b260428
|
agent-framework-orchestrations==1.0.0b260428
|
||||||
agent-framework-purview==1.0.0b260428
|
agent-framework-purview==1.0.0b260428
|
||||||
agent-framework-redis==1.0.0b260428
|
agent-framework-redis==1.0.0b260428
|
||||||
|
|||||||
@@ -11,6 +11,6 @@ httpx
|
|||||||
pywebpush
|
pywebpush
|
||||||
py-vapid
|
py-vapid
|
||||||
python-dateutil
|
python-dateutil
|
||||||
agent-framework==1.2.1
|
agent-framework==1.10.0
|
||||||
agent-framework-ag-ui==1.0.0b260428
|
agent-framework-ag-ui==1.0.0rc7
|
||||||
agent-framework-openai==1.2.1
|
agent-framework-openai==1.10.0
|
||||||
|
|||||||
131
backend/tests/test_chat_persistence.py
Normal file
131
backend/tests/test_chat_persistence.py
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
"""Chat thread persistence: snapshot store round-trip + clear endpoint."""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
|
||||||
|
from agent_framework_ag_ui import AGUIThreadSnapshot
|
||||||
|
from sqlmodel import select
|
||||||
|
|
||||||
|
import app.agent.snapshot_store as snapshot_store_module
|
||||||
|
from app.agent.snapshot_store import PostgresSnapshotStore
|
||||||
|
from app.api.v1.endpoints.chat import clear_thread
|
||||||
|
from app.models.models import ChatThreadSnapshot
|
||||||
|
|
||||||
|
|
||||||
|
def _store(monkeypatch, engine):
|
||||||
|
# The store opens its own sessions from app.db.engine; point it at the
|
||||||
|
# test engine instead.
|
||||||
|
monkeypatch.setattr(snapshot_store_module, "engine", engine)
|
||||||
|
return PostgresSnapshotStore()
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_get_roundtrip_normalizes_and_filters(monkeypatch, engine):
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
snapshot = AGUIThreadSnapshot(
|
||||||
|
messages=[
|
||||||
|
{"id": "u1", "role": "user", "content": "hola"},
|
||||||
|
{
|
||||||
|
"id": "a1",
|
||||||
|
"role": "assistant",
|
||||||
|
"content": "",
|
||||||
|
# MAF emits snake_case; the store must return camelCase so
|
||||||
|
# @ag-ui/client's Zod schema doesn't strip it.
|
||||||
|
"tool_calls": [{"id": "t1", "type": "function"}],
|
||||||
|
},
|
||||||
|
{"id": "t1r", "role": "tool", "content": "ok", "tool_call_id": "t1"},
|
||||||
|
# MAF writes tool_calls: null on plain-text assistant messages —
|
||||||
|
# must not survive as toolCalls: null in the replay.
|
||||||
|
{"id": "a2", "role": "assistant", "content": "done", "tool_calls": None},
|
||||||
|
{"id": "ctx-123", "role": "system", "content": "run context"},
|
||||||
|
],
|
||||||
|
state={"k": "v"},
|
||||||
|
)
|
||||||
|
asyncio.run(store.save(scope="default", thread_id="main", snapshot=snapshot))
|
||||||
|
|
||||||
|
loaded = asyncio.run(store.get(scope="default", thread_id="main"))
|
||||||
|
assert loaded is not None
|
||||||
|
assert [m["id"] for m in loaded.messages] == ["u1", "a1", "t1r", "a2"]
|
||||||
|
assistant = loaded.messages[1]
|
||||||
|
assert "tool_calls" not in assistant
|
||||||
|
assert assistant["toolCalls"] == [{"id": "t1", "type": "function"}]
|
||||||
|
tool_msg = loaded.messages[2]
|
||||||
|
assert "tool_call_id" not in tool_msg
|
||||||
|
assert tool_msg["toolCallId"] == "t1"
|
||||||
|
text_msg = loaded.messages[3]
|
||||||
|
assert "toolCalls" not in text_msg and "tool_calls" not in text_msg
|
||||||
|
assert loaded.state == {"k": "v"}
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_upserts_single_row(monkeypatch, engine, session):
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
for n in range(3):
|
||||||
|
snap = AGUIThreadSnapshot(
|
||||||
|
messages=[{"id": f"u{i}", "role": "user", "content": str(i)} for i in range(n + 1)]
|
||||||
|
)
|
||||||
|
asyncio.run(store.save(scope="default", thread_id="main", snapshot=snap))
|
||||||
|
|
||||||
|
rows = session.exec(select(ChatThreadSnapshot)).all()
|
||||||
|
assert len(rows) == 1
|
||||||
|
assert len(rows[0].messages) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_get_unknown_thread_returns_none(monkeypatch, engine):
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
assert asyncio.run(store.get(scope="default", thread_id="nope")) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_delete_and_scope_isolation(monkeypatch, engine):
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
snap = AGUIThreadSnapshot(messages=[{"id": "u1", "role": "user", "content": "x"}])
|
||||||
|
asyncio.run(store.save(scope="default", thread_id="main", snapshot=snap))
|
||||||
|
|
||||||
|
assert asyncio.run(store.get(scope="other", thread_id="main")) is None
|
||||||
|
assert asyncio.run(store.delete(scope="default", thread_id="main")) is True
|
||||||
|
assert asyncio.run(store.delete(scope="default", thread_id="main")) is False
|
||||||
|
assert asyncio.run(store.get(scope="default", thread_id="main")) is None
|
||||||
|
|
||||||
|
|
||||||
|
def test_save_dedupes_multi_run_turn(monkeypatch, engine):
|
||||||
|
"""Mirror of MAF's duplicate merge on frontend-tool roundtrips: repeated
|
||||||
|
message ids, a re-issued identical render call, and extra tool results."""
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
|
||||||
|
def call(cid, name, args='{"a":1}'):
|
||||||
|
return {"id": cid, "type": "function", "function": {"name": name, "arguments": args}}
|
||||||
|
|
||||||
|
snapshot = AGUIThreadSnapshot(
|
||||||
|
messages=[
|
||||||
|
{"id": "u1", "role": "user", "content": "resumen"},
|
||||||
|
{"id": "a1", "role": "assistant", "content": "",
|
||||||
|
"tool_calls": [call("c1", "get_cycle_summary"), call("c2", "render_spending_summary")]},
|
||||||
|
{"id": "t1", "role": "tool", "content": "data", "tool_call_id": "c1"},
|
||||||
|
# MAF re-records the resent suffix verbatim…
|
||||||
|
{"id": "u1", "role": "user", "content": "resumen"},
|
||||||
|
{"id": "t1", "role": "tool", "content": "data", "tool_call_id": "c1"},
|
||||||
|
{"id": "t2", "role": "tool", "content": "ok", "tool_call_id": "c2"},
|
||||||
|
# …plus a synthetic second result for an answered call…
|
||||||
|
{"id": "t3", "role": "tool", "content": "", "tool_call_id": "c1"},
|
||||||
|
# …and the model re-issues the same render call with a new id.
|
||||||
|
{"id": "a2", "role": "assistant", "content": "",
|
||||||
|
"tool_calls": [call("c3", "render_spending_summary")]},
|
||||||
|
{"id": "t4", "role": "tool", "content": "ok", "tool_call_id": "c3"},
|
||||||
|
]
|
||||||
|
)
|
||||||
|
asyncio.run(store.save(scope="default", thread_id="main", snapshot=snapshot))
|
||||||
|
|
||||||
|
loaded = asyncio.run(store.get(scope="default", thread_id="main"))
|
||||||
|
assert [m["id"] for m in loaded.messages] == ["u1", "a1", "t1", "t2"]
|
||||||
|
# The duplicate render call (c3) and its result are gone; each surviving
|
||||||
|
# call has exactly one result.
|
||||||
|
assert [tc["id"] for tc in loaded.messages[1]["toolCalls"]] == ["c1", "c2"]
|
||||||
|
assert [(m["toolCallId"]) for m in loaded.messages if m["role"] == "tool"] == ["c1", "c2"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_clear_thread_endpoint(monkeypatch, engine, session):
|
||||||
|
store = _store(monkeypatch, engine)
|
||||||
|
assert clear_thread(session=session, _user="testadmin") == {"cleared": False}
|
||||||
|
|
||||||
|
snap = AGUIThreadSnapshot(messages=[{"id": "u1", "role": "user", "content": "x"}])
|
||||||
|
asyncio.run(store.save(scope="default", thread_id="main", snapshot=snap))
|
||||||
|
|
||||||
|
assert clear_thread(session=session, _user="testadmin") == {"cleared": True}
|
||||||
|
assert asyncio.run(store.get(scope="default", thread_id="main")) is None
|
||||||
@@ -32,6 +32,8 @@ services:
|
|||||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY}
|
||||||
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
OPENAI_API_KEY: ${OPENAI_API_KEY}
|
||||||
AGENT_MODEL: ${AGENT_MODEL:-gpt-5.4-mini}
|
AGENT_MODEL: ${AGENT_MODEL:-gpt-5.4-mini}
|
||||||
|
# MAF 1.6+ enables OTel instrumentation by default; keep it off explicitly.
|
||||||
|
ENABLE_INSTRUMENTATION: "false"
|
||||||
expose:
|
expose:
|
||||||
- "8000"
|
- "8000"
|
||||||
networks:
|
networks:
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ services:
|
|||||||
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
VAPID_PUBLIC_KEY: ${VAPID_PUBLIC_KEY:-}
|
||||||
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
OPENAI_API_KEY: ${OPENAI_API_KEY:-}
|
||||||
AGENT_MODEL: ${AGENT_MODEL:-gpt-5.4-mini}
|
AGENT_MODEL: ${AGENT_MODEL:-gpt-5.4-mini}
|
||||||
|
# MAF 1.6+ enables OTel instrumentation by default; keep it off explicitly.
|
||||||
|
ENABLE_INSTRUMENTATION: "false"
|
||||||
ports:
|
ports:
|
||||||
- "8001:8000"
|
- "8001:8000"
|
||||||
volumes:
|
volumes:
|
||||||
|
|||||||
@@ -12,11 +12,11 @@
|
|||||||
"gen:api": "openapi-typescript openapi.json -o src/lib/api-types.gen.ts"
|
"gen:api": "openapi-typescript openapi.json -o src/lib/api-types.gen.ts"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@ag-ui/client": "0.0.52",
|
"@ag-ui/client": "0.0.57",
|
||||||
"@base-ui/react": "^1.4.1",
|
"@base-ui/react": "^1.4.1",
|
||||||
"@copilotkit/react-core": "1.56.4",
|
"@copilotkit/react-core": "1.62.2",
|
||||||
"@copilotkit/react-ui": "1.56.4",
|
"@copilotkit/react-ui": "1.62.2",
|
||||||
"@copilotkit/runtime": "1.56.4",
|
"@copilotkit/runtime": "1.62.2",
|
||||||
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
|
"@fontsource-variable/ibm-plex-sans": "^5.2.8",
|
||||||
"@fontsource-variable/noto-sans": "^5.2.10",
|
"@fontsource-variable/noto-sans": "^5.2.10",
|
||||||
"@hono/node-server": "^1.14.4",
|
"@hono/node-server": "^1.14.4",
|
||||||
|
|||||||
297
frontend/pnpm-lock.yaml
generated
297
frontend/pnpm-lock.yaml
generated
@@ -9,20 +9,20 @@ importers:
|
|||||||
.:
|
.:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client':
|
'@ag-ui/client':
|
||||||
specifier: 0.0.52
|
specifier: 0.0.57
|
||||||
version: 0.0.52
|
version: 0.0.57
|
||||||
'@base-ui/react':
|
'@base-ui/react':
|
||||||
specifier: ^1.4.1
|
specifier: ^1.4.1
|
||||||
version: 1.4.1(@date-fns/tz@1.5.0)(@types/react@19.2.14)(date-fns@4.4.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
version: 1.4.1(@date-fns/tz@1.5.0)(@types/react@19.2.14)(date-fns@4.4.0)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
'@copilotkit/react-core':
|
'@copilotkit/react-core':
|
||||||
specifier: 1.56.4
|
specifier: 1.62.2
|
||||||
version: 1.56.4(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
version: 1.62.2(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
||||||
'@copilotkit/react-ui':
|
'@copilotkit/react-ui':
|
||||||
specifier: 1.56.4
|
specifier: 1.62.2
|
||||||
version: 1.56.4(@ag-ui/core@0.0.52)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
version: 1.62.2(@ag-ui/core@0.0.57)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
||||||
'@copilotkit/runtime':
|
'@copilotkit/runtime':
|
||||||
specifier: 1.56.4
|
specifier: 1.62.2
|
||||||
version: 1.56.4(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@langchain/langgraph-sdk@1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@opentelemetry/api@1.9.0)(langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
version: 1.62.2(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@langchain/langgraph-sdk@1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@opentelemetry/api@1.9.0)(langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
||||||
'@fontsource-variable/ibm-plex-sans':
|
'@fontsource-variable/ibm-plex-sans':
|
||||||
specifier: ^5.2.8
|
specifier: ^5.2.8
|
||||||
version: 5.2.8
|
version: 5.2.8
|
||||||
@@ -131,23 +131,35 @@ packages:
|
|||||||
'@a2ui/web_core@0.9.0':
|
'@a2ui/web_core@0.9.0':
|
||||||
resolution: {integrity: sha512-TsMWuEeuVDsScGIGPy/fWIZu+EOBRfhx6KwjKh3VwY1AwysRenQM8zDr8VrSk14Wck/aBgVxk2zWVrMCK2/s6A==}
|
resolution: {integrity: sha512-TsMWuEeuVDsScGIGPy/fWIZu+EOBRfhx6KwjKh3VwY1AwysRenQM8zDr8VrSk14Wck/aBgVxk2zWVrMCK2/s6A==}
|
||||||
|
|
||||||
'@ag-ui/a2ui-middleware@0.0.5':
|
'@ag-ui/a2ui-middleware@0.0.10':
|
||||||
resolution: {integrity: sha512-nhF8LBzq36BmLR1O3nStxqSpIu1+4//ttqOyQgiXUsKZK1znCyN4q4MBTARrkFa7CuFr3fcNTiZuS4V1HRUHTw==}
|
resolution: {integrity: sha512-2BQFUQ9vJzUAQSR0dNW/ijhyH8KpiRWISSLTP6mIe6ENyQ2cM1/XLG38/Dcb69olcs36gtZADZzAQWcno5H6fA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ag-ui/client': '>=0.0.40'
|
'@ag-ui/client': '>=0.0.40'
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
|
|
||||||
'@ag-ui/client@0.0.52':
|
'@ag-ui/a2ui-toolkit@0.0.4':
|
||||||
resolution: {integrity: sha512-U407VvDDwR5qs8TiyN1qY38x87qMWc2n0epw8iA5aa1qwzCKBBDgg3Fkm4JogQf0X4jwNsz8HUbIZrBB56mrpg==}
|
resolution: {integrity: sha512-9VPmgpCAsFVICk7z23kh+Kp/BwYMxw0D0KGjOiKXhm1MAH+Wid2iC3yKsXso+q7UZZiyhWgeDUSgaAkltyLmGg==}
|
||||||
|
|
||||||
'@ag-ui/core@0.0.52':
|
'@ag-ui/client@0.0.54':
|
||||||
resolution: {integrity: sha512-Xo0bUaNV56EqylzcrAuhUkQX7et7+SZIrqZZtEByGwEq/I1EHny6ZMkWHLkKR7UNi0FJZwJyhKYmKJS3B2SEgA==}
|
resolution: {integrity: sha512-N5UVXEBV5gPHqTuMoR/21brconRn42URf+MB4L8OniCJKqLcl/qUJb5kMamK0nnfBhDfPs/uq7LxDn6bsDJzJg==}
|
||||||
|
|
||||||
'@ag-ui/encoder@0.0.52':
|
'@ag-ui/client@0.0.57':
|
||||||
resolution: {integrity: sha512-6GVDTb1dv2rjap7VVnmXYypDutZi6nrsTcdfxoP6ryDG5ynlXtmmS+FSDAt62JbIMD5CtEE963xNCb6d1iXw9g==}
|
resolution: {integrity: sha512-Xap2alG9Z0/j5kb3x4D7oTpe2sw1dfrC9rgJJr2NZu5vKcm8dzIPNd31mF2B4zS3BKqYIu245yxKPhEtT30MHw==}
|
||||||
|
|
||||||
'@ag-ui/langgraph@0.0.29':
|
'@ag-ui/core@0.0.54':
|
||||||
resolution: {integrity: sha512-7Q4YMtuI53N3uyZbEhYsIpfwYK1l5mWX88nm7BE242lIpPsiLIj3RL8xpT8lojqmR9MyZ72jlHIhXm1hbsrZjg==}
|
resolution: {integrity: sha512-Ilx31OvRQaZfU7jSArGqz06JZKOsAt8zWiCPJljyp9zR6Tzl18oyfx8o6FsuGfAktGRe50GI9SCCxNXXysZwtA==}
|
||||||
|
|
||||||
|
'@ag-ui/core@0.0.57':
|
||||||
|
resolution: {integrity: sha512-gho1OWjNE6E3Rl7ZEZ1wr2CEpUHjLFU0FqzCZZk439TicLu+BfLCMkMokB07bMGlRmbJ60hM6LW60iOVauCx+Q==}
|
||||||
|
|
||||||
|
'@ag-ui/encoder@0.0.54':
|
||||||
|
resolution: {integrity: sha512-0dPuE/eAeBRBDj/OOj5AW8SoP1r0dufmoOdrtKgmf+dlbVXKSNkDDHGrrvIWFPxwvPTWhHeN6wnsVUayWpUsGg==}
|
||||||
|
|
||||||
|
'@ag-ui/encoder@0.0.57':
|
||||||
|
resolution: {integrity: sha512-ifD9NctR4xyPDR58xF9GK1bj/S8oECFkTeDfuYD8tXdbcOstIJ2TOqU2zhiCKnw7Vw+zR9Qv3TbsM9E7Gi9X3Q==}
|
||||||
|
|
||||||
|
'@ag-ui/langgraph@0.0.42':
|
||||||
|
resolution: {integrity: sha512-dXasEbGQFJcasdoy5khYyDHZHYoD1/i7hioaP8cejfw+Dss4tLvN8ndzD6c5j0hmANaKscekl+zkF7UQq3sI9w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ag-ui/client': '>=0.0.42'
|
'@ag-ui/client': '>=0.0.42'
|
||||||
'@ag-ui/core': '>=0.0.42'
|
'@ag-ui/core': '>=0.0.42'
|
||||||
@@ -157,8 +169,16 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ag-ui/client': '>=0.0.40'
|
'@ag-ui/client': '>=0.0.40'
|
||||||
|
|
||||||
'@ag-ui/proto@0.0.52':
|
'@ag-ui/mcp-middleware@0.0.1':
|
||||||
resolution: {integrity: sha512-+iCGzNUNL50YIoThVmsolWPjG4MJidl+R9k8QAGVwErEfHRtQ64KFyrdpeOXNVuWtM3SViJqPSgFyv7eGVS63A==}
|
resolution: {integrity: sha512-TayUu7kB+jXUTPRUJesNvJYrP+0weTL9F2VJJ8QQ4sWxY/Ihjo+GgFYgJZYNcLwbo1DKgmVJtdm2XUouPCbxeg==}
|
||||||
|
peerDependencies:
|
||||||
|
rxjs: 7.8.1
|
||||||
|
|
||||||
|
'@ag-ui/proto@0.0.54':
|
||||||
|
resolution: {integrity: sha512-IPF+xeFaBAKKP2FO74MaVTkKUP8VaGGkbPzORCvC5TLDdGs+oQgQFqz+XoBeksQGE14+jgLWiAr9EPXdhqr1NA==}
|
||||||
|
|
||||||
|
'@ag-ui/proto@0.0.57':
|
||||||
|
resolution: {integrity: sha512-pPENOZt0P6ibH8sCTgq05wLYXi5t3P9B5r/1bWYehXjUxtyOdnukSlWM++SsCIwUXsQdm/b3aBgGjEeTF7RenA==}
|
||||||
|
|
||||||
'@ai-sdk/anthropic@2.0.77':
|
'@ai-sdk/anthropic@2.0.77':
|
||||||
resolution: {integrity: sha512-8n7ApEzFOxqVvT3HyqLrEQlgUx/2nUmPFLTGY3fNKwUA8KVNU3Ovd2C66Qh1Y93Iq5NkHsOWuLiTyAZpRKQhgw==}
|
resolution: {integrity: sha512-8n7ApEzFOxqVvT3HyqLrEQlgUx/2nUmPFLTGY3fNKwUA8KVNU3Ovd2C66Qh1Y93Iq5NkHsOWuLiTyAZpRKQhgw==}
|
||||||
@@ -300,38 +320,43 @@ packages:
|
|||||||
'@chevrotain/utils@12.0.0':
|
'@chevrotain/utils@12.0.0':
|
||||||
resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==}
|
resolution: {integrity: sha512-lB59uJoaGIfOOL9knQqQRfhl9g7x8/wqFkp13zTdkRu1huG9kg6IJs1O8hqj9rs6h7orGxHJUKb+mX3rPbWGhA==}
|
||||||
|
|
||||||
'@copilotkit/a2ui-renderer@1.56.4':
|
'@copilotkit/a2ui-renderer@1.62.2':
|
||||||
resolution: {integrity: sha512-eYikRML0l8p9zrNYm6SW5YO3PygmQPM8QcBcvq7kbRrvlJR7LP1qT+sbkzK8dUEU7k5WrE63BEOgLRNVscn60w==}
|
resolution: {integrity: sha512-9V7s3aSFGBWS65EXdP04Fg597x9g1WpwoBT6mHW2Ev2uvIzo84KhObw2jWpNsaecAheesP70xS7752FB4GExxw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||||
|
peerDependenciesMeta:
|
||||||
|
react:
|
||||||
|
optional: true
|
||||||
|
react-dom:
|
||||||
|
optional: true
|
||||||
|
|
||||||
'@copilotkit/core@1.56.4':
|
'@copilotkit/core@1.62.2':
|
||||||
resolution: {integrity: sha512-fCOhaxmNTgeCJRs1IDOmKoMXcAOJmTlWzYPum/vi6xuXM7M6+3OAtlDAed/47FVAxqny72SWCynBudH8hKwBQg==}
|
resolution: {integrity: sha512-I3fCb6nZVtRi8+HuJULD0RzgTv6ZxkNZtrz+Ewd1lwhLUmw0gBsmsv1HitsM6opayuAZj+4mVrhwhgKJyZwSQQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@copilotkit/license-verifier@0.2.0':
|
'@copilotkit/license-verifier@0.5.0':
|
||||||
resolution: {integrity: sha512-hliCifqy5a65YTozgRckuQmvBEQlt4L2PhbpSDY6fb/TKqPHyNDJgItRSnOpxOVDqvEfHEbUUqw3NaD88ZtdJA==}
|
resolution: {integrity: sha512-vrwKtIpYwF0FT9ZoYASH8owa2cGV0dhDvJGaCRaRMStwDxpc6DRdydKkhx8cWZXyBRxEYcq/Vygv4JvevhQQdQ==}
|
||||||
|
|
||||||
'@copilotkit/react-core@1.56.4':
|
'@copilotkit/react-core@1.62.2':
|
||||||
resolution: {integrity: sha512-HObN7YSX3KaCN+Dg0WzXHBylayI3WWQKCFB/DMsGajWefd2+Y3I33gs+hCKQFDcD/l8IWpR1/XvmGfzo6kvxdQ==}
|
resolution: {integrity: sha512-tw3/PMzP4NEEdkBrK3PVJbkbh1ztnun9WdBHObLyU/Ou2d/37Rm3w5cnt17WVIxTa1FLtMoSdp3kC26656RAbg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||||
zod: '>=3.0.0'
|
zod: '>=3.0.0'
|
||||||
|
|
||||||
'@copilotkit/react-ui@1.56.4':
|
'@copilotkit/react-ui@1.62.2':
|
||||||
resolution: {integrity: sha512-pl2LZSYFOo6DjEiJDX/IstGqgvPn/xfLVzKkKyWuiJqxs69jynQIziCdUblbpV1d47hEqJOGgiAc7z7ltATLMA==}
|
resolution: {integrity: sha512-lFbf/7GiSpu3WFk0+Fbd6v4eqNx3n2B3vYZle2hF+7YM1c/jtlll6wM4JlB0CNtxFvAbos5sNifyuMiliF6bbg==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
|
|
||||||
'@copilotkit/runtime-client-gql@1.56.4':
|
'@copilotkit/runtime-client-gql@1.62.2':
|
||||||
resolution: {integrity: sha512-XHE45+Gz5vlMNuTt8noF/0Pc22GqAO68oU4O/J2ktKjI25WUBTvakbS2cgXELhxJszum6JeFjMyMvQpurXQukg==}
|
resolution: {integrity: sha512-ZDPsMAFk6BFd0tfDJd7kbmxmMErdulcWLWaoVhWLhoBunNcKaq/V487vel3sOy5IkQPM2xia5Vh9GJeirMTo+w==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
|
|
||||||
'@copilotkit/runtime@1.56.4':
|
'@copilotkit/runtime@1.62.2':
|
||||||
resolution: {integrity: sha512-WLupYn1K3hK7xkBY4MdM2lIhoyrGf+3K68fTkRUVc7fa5gADI0WjbRtBJnNwSUPFOBpVTTPAokqFl2+GdC1oEw==}
|
resolution: {integrity: sha512-sZg38j0MGYlIAMjdUBvkSPoOGfucV3pSHG3wne783l9LyQMrOjLpDoT1DKixdybJg+6sSv84iEJlsQhR7UXiEA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@anthropic-ai/sdk': ^0.57.0
|
'@anthropic-ai/sdk': ^0.57.0
|
||||||
'@langchain/aws': '>=0.1.9'
|
'@langchain/aws': '>=0.1.9'
|
||||||
@@ -363,13 +388,19 @@ packages:
|
|||||||
openai:
|
openai:
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@copilotkit/shared@1.56.4':
|
'@copilotkit/shared@1.62.2':
|
||||||
resolution: {integrity: sha512-sHmjRyZkmbHqhbK0OGT66gtCHAMA2AIWtNEFb2nwStzVu64a5dNhaayzpu7VbLpfkzeNzeWCPmZecdrN/EK1wQ==}
|
resolution: {integrity: sha512-MLSvQIq7q8Z3FbQaqcSYR2i5sDvKIconidCXLbmZYIwdfbjnjPFjHzJM8PU9WTxJsYwQ3iM6/EysEm+5+bV4yQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@ag-ui/core': '>=0.0.48'
|
'@ag-ui/core': '>=0.0.48'
|
||||||
|
|
||||||
'@copilotkit/web-inspector@1.56.4':
|
'@copilotkit/web-components@1.62.2':
|
||||||
resolution: {integrity: sha512-0PS1AnrTHbmvctveYuRrMr+CQ1FqiRQFhUBcDhSmz2eINr00dOybqIr8xXW78LD6E/6kyuLpTfaGHQLtBftMNw==}
|
resolution: {integrity: sha512-JHqIakneTlGXR9X0TdgmfbttsdT6ztifJvDM642YrTDR5+nv5DLJoAX0V36XxH3CsbpIMU/GrTS9YyPs1bJgrg==}
|
||||||
|
engines: {node: '>=18'}
|
||||||
|
peerDependencies:
|
||||||
|
lit: ^3.3.2
|
||||||
|
|
||||||
|
'@copilotkit/web-inspector@1.62.2':
|
||||||
|
resolution: {integrity: sha512-OUYmtN1VrsgykYGMFnGgvBZy2HuaOi/gCjWn6yZiSCkhOOHfSRk0kvRe0605RDObSamspVIPWfVNGimPe56WcQ==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
|
|
||||||
'@date-fns/tz@1.5.0':
|
'@date-fns/tz@1.5.0':
|
||||||
@@ -1103,15 +1134,6 @@ packages:
|
|||||||
'@types/react':
|
'@types/react':
|
||||||
optional: true
|
optional: true
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.2.4':
|
|
||||||
resolution: {integrity: sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA==}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': '*'
|
|
||||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.3.0':
|
'@radix-ui/react-slot@1.3.0':
|
||||||
resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==}
|
resolution: {integrity: sha512-MojKku4U/miO8Av4Dkb+ctMAQx7JmY96LmtDQlAarCRtd7rN52QCSzBF+XAvr5S6coSVj9HEPBgHAHKEJVk/WA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1729,6 +1751,7 @@ packages:
|
|||||||
|
|
||||||
'@ungap/structured-clone@1.3.0':
|
'@ungap/structured-clone@1.3.0':
|
||||||
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
resolution: {integrity: sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==}
|
||||||
|
deprecated: Potential CWE-502 - Update to 1.3.1 or higher
|
||||||
|
|
||||||
'@upsetjs/venn.js@2.0.0':
|
'@upsetjs/venn.js@2.0.0':
|
||||||
resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
|
resolution: {integrity: sha512-WbBhLrooyePuQ1VZxrJjtLvTc4NVfpOyKx0sKqioq9bX1C1m7Jgykkn8gLrtwumBioXIqam8DLxp88Adbue6Hw==}
|
||||||
@@ -4152,6 +4175,7 @@ packages:
|
|||||||
|
|
||||||
uuid@10.0.0:
|
uuid@10.0.0:
|
||||||
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
resolution: {integrity: sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ==}
|
||||||
|
deprecated: uuid@10 and below is no longer supported. For ESM codebases, update to uuid@latest. For CommonJS codebases, use uuid@11 (but be aware this version will likely be deprecated in 2028).
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
uuid@11.1.0:
|
uuid@11.1.0:
|
||||||
@@ -4303,17 +4327,20 @@ snapshots:
|
|||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
||||||
|
|
||||||
'@ag-ui/a2ui-middleware@0.0.5(@ag-ui/client@0.0.52)(rxjs@7.8.1)':
|
'@ag-ui/a2ui-middleware@0.0.10(@ag-ui/client@0.0.57)(rxjs@7.8.1)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/a2ui-toolkit': 0.0.4
|
||||||
|
'@ag-ui/client': 0.0.57
|
||||||
clarinet: 0.12.6
|
clarinet: 0.12.6
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
|
|
||||||
'@ag-ui/client@0.0.52':
|
'@ag-ui/a2ui-toolkit@0.0.4': {}
|
||||||
|
|
||||||
|
'@ag-ui/client@0.0.54':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/core': 0.0.54
|
||||||
'@ag-ui/encoder': 0.0.52
|
'@ag-ui/encoder': 0.0.54
|
||||||
'@ag-ui/proto': 0.0.52
|
'@ag-ui/proto': 0.0.54
|
||||||
'@types/uuid': 10.0.0
|
'@types/uuid': 10.0.0
|
||||||
compare-versions: 6.1.1
|
compare-versions: 6.1.1
|
||||||
fast-json-patch: 3.1.1
|
fast-json-patch: 3.1.1
|
||||||
@@ -4322,19 +4349,42 @@ snapshots:
|
|||||||
uuid: 11.1.0
|
uuid: 11.1.0
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
|
|
||||||
'@ag-ui/core@0.0.52':
|
'@ag-ui/client@0.0.57':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/core': 0.0.57
|
||||||
|
'@ag-ui/encoder': 0.0.57
|
||||||
|
'@ag-ui/proto': 0.0.57
|
||||||
|
'@types/uuid': 10.0.0
|
||||||
|
compare-versions: 6.1.1
|
||||||
|
fast-json-patch: 3.1.1
|
||||||
|
rxjs: 7.8.1
|
||||||
|
untruncate-json: 0.0.1
|
||||||
|
uuid: 11.1.0
|
||||||
|
zod: 3.25.76
|
||||||
|
|
||||||
|
'@ag-ui/core@0.0.54':
|
||||||
dependencies:
|
dependencies:
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
|
|
||||||
'@ag-ui/encoder@0.0.52':
|
'@ag-ui/core@0.0.57':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/core': 0.0.52
|
zod: 3.25.76
|
||||||
'@ag-ui/proto': 0.0.52
|
|
||||||
|
|
||||||
'@ag-ui/langgraph@0.0.29(@ag-ui/client@0.0.52)(@ag-ui/core@0.0.52)(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))':
|
'@ag-ui/encoder@0.0.54':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/core': 0.0.54
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/proto': 0.0.54
|
||||||
|
|
||||||
|
'@ag-ui/encoder@0.0.57':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/core': 0.0.57
|
||||||
|
'@ag-ui/proto': 0.0.57
|
||||||
|
|
||||||
|
'@ag-ui/langgraph@0.0.42(@ag-ui/client@0.0.57)(@ag-ui/core@0.0.57)(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/a2ui-toolkit': 0.0.4
|
||||||
|
'@ag-ui/client': 0.0.57
|
||||||
|
'@ag-ui/core': 0.0.57
|
||||||
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
'@langchain/langgraph-sdk': 1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@langchain/langgraph-sdk': 1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
langchain: 1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
langchain: 1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
||||||
@@ -4352,9 +4402,9 @@ snapshots:
|
|||||||
- ws
|
- ws
|
||||||
- zod-to-json-schema
|
- zod-to-json-schema
|
||||||
|
|
||||||
'@ag-ui/mcp-apps-middleware@0.0.3(@ag-ui/client@0.0.52)(@cfworker/json-schema@4.1.1)(zod@3.25.76)':
|
'@ag-ui/mcp-apps-middleware@0.0.3(@ag-ui/client@0.0.57)(@cfworker/json-schema@4.1.1)(zod@3.25.76)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/client': 0.0.57
|
||||||
'@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76)
|
'@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76)
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -4362,9 +4412,25 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- zod
|
- zod
|
||||||
|
|
||||||
'@ag-ui/proto@0.0.52':
|
'@ag-ui/mcp-middleware@0.0.1(@cfworker/json-schema@4.1.1)(rxjs@7.8.1)(zod@3.25.76)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/client': 0.0.54
|
||||||
|
'@modelcontextprotocol/sdk': 1.29.0(@cfworker/json-schema@4.1.1)(zod@3.25.76)
|
||||||
|
rxjs: 7.8.1
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- '@cfworker/json-schema'
|
||||||
|
- supports-color
|
||||||
|
- zod
|
||||||
|
|
||||||
|
'@ag-ui/proto@0.0.54':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/core': 0.0.54
|
||||||
|
'@bufbuild/protobuf': 2.11.0
|
||||||
|
'@protobuf-ts/protoc': 2.11.1
|
||||||
|
|
||||||
|
'@ag-ui/proto@0.0.57':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/core': 0.0.57
|
||||||
'@bufbuild/protobuf': 2.11.0
|
'@bufbuild/protobuf': 2.11.0
|
||||||
'@protobuf-ts/protoc': 2.11.1
|
'@protobuf-ts/protoc': 2.11.1
|
||||||
|
|
||||||
@@ -4513,19 +4579,21 @@ snapshots:
|
|||||||
|
|
||||||
'@chevrotain/utils@12.0.0': {}
|
'@chevrotain/utils@12.0.0': {}
|
||||||
|
|
||||||
'@copilotkit/a2ui-renderer@1.56.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
'@copilotkit/a2ui-renderer@1.62.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@a2ui/web_core': 0.9.0
|
'@a2ui/web_core': 0.9.0
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
react: 19.2.5
|
lit: 3.3.2
|
||||||
react-dom: 19.2.5(react@19.2.5)
|
|
||||||
zod: 3.25.76
|
zod: 3.25.76
|
||||||
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
zod-to-json-schema: 3.25.2(zod@3.25.76)
|
||||||
|
optionalDependencies:
|
||||||
|
react: 19.2.5
|
||||||
|
react-dom: 19.2.5(react@19.2.5)
|
||||||
|
|
||||||
'@copilotkit/core@1.56.4(@ag-ui/core@0.0.52)(zod@4.3.6)':
|
'@copilotkit/core@1.62.2(@ag-ui/core@0.0.57)(zod@4.3.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/client': 0.0.57
|
||||||
'@copilotkit/shared': 1.56.4(@ag-ui/core@0.0.52)
|
'@copilotkit/shared': 1.62.2(@ag-ui/core@0.0.57)
|
||||||
'@tanstack/pacer': 0.20.1
|
'@tanstack/pacer': 0.20.1
|
||||||
phoenix: 1.8.5
|
phoenix: 1.8.5
|
||||||
rxjs: 7.8.1
|
rxjs: 7.8.1
|
||||||
@@ -4535,27 +4603,29 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- zod
|
- zod
|
||||||
|
|
||||||
'@copilotkit/license-verifier@0.2.0': {}
|
'@copilotkit/license-verifier@0.5.0': {}
|
||||||
|
|
||||||
'@copilotkit/react-core@1.56.4(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)':
|
'@copilotkit/react-core@1.62.2(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/client': 0.0.57
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/core': 0.0.57
|
||||||
'@copilotkit/a2ui-renderer': 1.56.4(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@copilotkit/a2ui-renderer': 1.62.2(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
'@copilotkit/core': 1.56.4(@ag-ui/core@0.0.52)(zod@4.3.6)
|
'@copilotkit/core': 1.62.2(@ag-ui/core@0.0.57)(zod@4.3.6)
|
||||||
'@copilotkit/runtime-client-gql': 1.56.4(@ag-ui/core@0.0.52)(graphql@16.13.2)(react@19.2.5)
|
'@copilotkit/runtime-client-gql': 1.62.2(@ag-ui/core@0.0.57)(graphql@16.13.2)(react@19.2.5)
|
||||||
'@copilotkit/shared': 1.56.4(@ag-ui/core@0.0.52)
|
'@copilotkit/shared': 1.62.2(@ag-ui/core@0.0.57)
|
||||||
'@copilotkit/web-inspector': 1.56.4(@ag-ui/core@0.0.52)(zod@4.3.6)
|
'@copilotkit/web-components': 1.62.2(lit@3.3.2)
|
||||||
|
'@copilotkit/web-inspector': 1.62.2(@ag-ui/core@0.0.57)(zod@4.3.6)
|
||||||
'@jetbrains/websandbox': 1.1.3
|
'@jetbrains/websandbox': 1.1.3
|
||||||
'@lit-labs/react': 2.1.3(@types/react@19.2.14)
|
'@lit-labs/react': 2.1.3(@types/react@19.2.14)
|
||||||
'@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@radix-ui/react-dropdown-menu': 2.1.16(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
'@radix-ui/react-slot': 1.2.4(@types/react@19.2.14)(react@19.2.5)
|
'@radix-ui/react-slot': 1.3.0(@types/react@19.2.14)(react@19.2.5)
|
||||||
'@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@radix-ui/react-tooltip': 1.2.8(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
'@scarf/scarf': 1.4.0
|
'@scarf/scarf': 1.4.0
|
||||||
'@tanstack/react-virtual': 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@tanstack/react-virtual': 3.13.24(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
class-variance-authority: 0.7.1
|
class-variance-authority: 0.7.1
|
||||||
clsx: 2.1.1
|
clsx: 2.1.1
|
||||||
katex: 0.16.45
|
katex: 0.16.45
|
||||||
|
lit: 3.3.2
|
||||||
lucide-react: 0.525.0(react@19.2.5)
|
lucide-react: 0.525.0(react@19.2.5)
|
||||||
react: 19.2.5
|
react: 19.2.5
|
||||||
react-dom: 19.2.5(react@19.2.5)
|
react-dom: 19.2.5(react@19.2.5)
|
||||||
@@ -4578,16 +4648,17 @@ snapshots:
|
|||||||
- micromark-util-types
|
- micromark-util-types
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@copilotkit/react-ui@1.56.4(@ag-ui/core@0.0.52)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)':
|
'@copilotkit/react-ui@1.62.2(@ag-ui/core@0.0.57)(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@copilotkit/react-core': 1.56.4(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
'@copilotkit/react-core': 1.62.2(@types/mdast@4.0.4)(@types/react-dom@19.2.3(@types/react@19.2.14))(@types/react@19.2.14)(graphql@16.13.2)(micromark-util-types@2.0.2)(micromark@4.0.2)(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod@4.3.6)
|
||||||
'@copilotkit/runtime-client-gql': 1.56.4(@ag-ui/core@0.0.52)(graphql@16.13.2)(react@19.2.5)
|
'@copilotkit/runtime-client-gql': 1.62.2(@ag-ui/core@0.0.57)(graphql@16.13.2)(react@19.2.5)
|
||||||
'@copilotkit/shared': 1.56.4(@ag-ui/core@0.0.52)
|
'@copilotkit/shared': 1.62.2(@ag-ui/core@0.0.57)
|
||||||
'@headlessui/react': 2.2.10(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@headlessui/react': 2.2.10(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
react: 19.2.5
|
react: 19.2.5
|
||||||
react-markdown: 10.1.0(@types/react@19.2.14)(react@19.2.5)
|
react-markdown: 10.1.0(@types/react@19.2.14)(react@19.2.5)
|
||||||
react-syntax-highlighter: 15.6.6(react@19.2.5)
|
react-syntax-highlighter: 15.6.6(react@19.2.5)
|
||||||
rehype-raw: 7.0.0
|
rehype-raw: 7.0.0
|
||||||
|
rehype-sanitize: 6.0.0
|
||||||
remark-gfm: 4.0.1
|
remark-gfm: 4.0.1
|
||||||
remark-math: 6.0.0
|
remark-math: 6.0.0
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -4603,9 +4674,9 @@ snapshots:
|
|||||||
- supports-color
|
- supports-color
|
||||||
- zod
|
- zod
|
||||||
|
|
||||||
'@copilotkit/runtime-client-gql@1.56.4(@ag-ui/core@0.0.52)(graphql@16.13.2)(react@19.2.5)':
|
'@copilotkit/runtime-client-gql@1.62.2(@ag-ui/core@0.0.57)(graphql@16.13.2)(react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@copilotkit/shared': 1.56.4(@ag-ui/core@0.0.52)
|
'@copilotkit/shared': 1.62.2(@ag-ui/core@0.0.57)
|
||||||
'@urql/core': 5.2.0(graphql@16.13.2)
|
'@urql/core': 5.2.0(graphql@16.13.2)
|
||||||
react: 19.2.5
|
react: 19.2.5
|
||||||
untruncate-json: 0.0.1
|
untruncate-json: 0.0.1
|
||||||
@@ -4615,21 +4686,22 @@ snapshots:
|
|||||||
- encoding
|
- encoding
|
||||||
- graphql
|
- graphql
|
||||||
|
|
||||||
'@copilotkit/runtime@1.56.4(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@langchain/langgraph-sdk@1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@opentelemetry/api@1.9.0)(langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))':
|
'@copilotkit/runtime@1.62.2(@cfworker/json-schema@4.1.1)(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@langchain/langgraph-sdk@1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5))(@opentelemetry/api@1.9.0)(langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)))(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/a2ui-middleware': 0.0.5(@ag-ui/client@0.0.52)(rxjs@7.8.1)
|
'@ag-ui/a2ui-middleware': 0.0.10(@ag-ui/client@0.0.57)(rxjs@7.8.1)
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/client': 0.0.57
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/core': 0.0.57
|
||||||
'@ag-ui/encoder': 0.0.52
|
'@ag-ui/encoder': 0.0.57
|
||||||
'@ag-ui/langgraph': 0.0.29(@ag-ui/client@0.0.52)(@ag-ui/core@0.0.52)(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
'@ag-ui/langgraph': 0.0.42(@ag-ui/client@0.0.57)(@ag-ui/core@0.0.57)(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6))
|
||||||
'@ag-ui/mcp-apps-middleware': 0.0.3(@ag-ui/client@0.0.52)(@cfworker/json-schema@4.1.1)(zod@3.25.76)
|
'@ag-ui/mcp-apps-middleware': 0.0.3(@ag-ui/client@0.0.57)(@cfworker/json-schema@4.1.1)(zod@3.25.76)
|
||||||
|
'@ag-ui/mcp-middleware': 0.0.1(@cfworker/json-schema@4.1.1)(rxjs@7.8.1)(zod@3.25.76)
|
||||||
'@ai-sdk/anthropic': 3.0.71(zod@3.25.76)
|
'@ai-sdk/anthropic': 3.0.71(zod@3.25.76)
|
||||||
'@ai-sdk/google': 3.0.64(zod@3.25.76)
|
'@ai-sdk/google': 3.0.64(zod@3.25.76)
|
||||||
'@ai-sdk/google-vertex': 3.0.132(zod@3.25.76)
|
'@ai-sdk/google-vertex': 3.0.132(zod@3.25.76)
|
||||||
'@ai-sdk/mcp': 1.0.36(zod@3.25.76)
|
'@ai-sdk/mcp': 1.0.36(zod@3.25.76)
|
||||||
'@ai-sdk/openai': 3.0.53(zod@3.25.76)
|
'@ai-sdk/openai': 3.0.53(zod@3.25.76)
|
||||||
'@copilotkit/license-verifier': 0.2.0
|
'@copilotkit/license-verifier': 0.5.0
|
||||||
'@copilotkit/shared': 1.56.4(@ag-ui/core@0.0.52)
|
'@copilotkit/shared': 1.62.2(@ag-ui/core@0.0.57)
|
||||||
'@graphql-yoga/plugin-defer-stream': 3.21.0(graphql-yoga@5.21.0(graphql@16.13.2))(graphql@16.13.2)
|
'@graphql-yoga/plugin-defer-stream': 3.21.0(graphql-yoga@5.21.0(graphql@16.13.2))(graphql@16.13.2)
|
||||||
'@hono/node-server': 1.19.14(hono@4.12.15)
|
'@hono/node-server': 1.19.14(hono@4.12.15)
|
||||||
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
@@ -4676,11 +4748,11 @@ snapshots:
|
|||||||
- vue
|
- vue
|
||||||
- zod-to-json-schema
|
- zod-to-json-schema
|
||||||
|
|
||||||
'@copilotkit/shared@1.56.4(@ag-ui/core@0.0.52)':
|
'@copilotkit/shared@1.62.2(@ag-ui/core@0.0.57)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
'@ag-ui/client': 0.0.57
|
||||||
'@ag-ui/core': 0.0.52
|
'@ag-ui/core': 0.0.57
|
||||||
'@copilotkit/license-verifier': 0.2.0
|
'@copilotkit/license-verifier': 0.5.0
|
||||||
'@segment/analytics-node': 2.3.0
|
'@segment/analytics-node': 2.3.0
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
chalk: 4.1.2
|
chalk: 4.1.2
|
||||||
@@ -4692,10 +4764,14 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- encoding
|
- encoding
|
||||||
|
|
||||||
'@copilotkit/web-inspector@1.56.4(@ag-ui/core@0.0.52)(zod@4.3.6)':
|
'@copilotkit/web-components@1.62.2(lit@3.3.2)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@ag-ui/client': 0.0.52
|
lit: 3.3.2
|
||||||
'@copilotkit/core': 1.56.4(@ag-ui/core@0.0.52)(zod@4.3.6)
|
|
||||||
|
'@copilotkit/web-inspector@1.62.2(@ag-ui/core@0.0.57)(zod@4.3.6)':
|
||||||
|
dependencies:
|
||||||
|
'@ag-ui/client': 0.0.57
|
||||||
|
'@copilotkit/core': 1.62.2(@ag-ui/core@0.0.57)(zod@4.3.6)
|
||||||
lit: 3.3.2
|
lit: 3.3.2
|
||||||
lucide: 0.525.0
|
lucide: 0.525.0
|
||||||
marked: 12.0.2
|
marked: 12.0.2
|
||||||
@@ -4979,7 +5055,7 @@ snapshots:
|
|||||||
langsmith: 0.5.25(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
langsmith: 0.5.25(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
mustache: 4.2.0
|
mustache: 4.2.0
|
||||||
p-queue: 6.6.2
|
p-queue: 6.6.2
|
||||||
zod: 3.25.76
|
zod: 4.3.6
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@opentelemetry/api'
|
- '@opentelemetry/api'
|
||||||
- '@opentelemetry/exporter-trace-otlp-proto'
|
- '@opentelemetry/exporter-trace-otlp-proto'
|
||||||
@@ -5003,14 +5079,14 @@ snapshots:
|
|||||||
react: 19.2.5
|
react: 19.2.5
|
||||||
react-dom: 19.2.5(react@19.2.5)
|
react-dom: 19.2.5(react@19.2.5)
|
||||||
|
|
||||||
'@langchain/langgraph@1.2.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@3.25.76)':
|
'@langchain/langgraph@1.2.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
'@langchain/langgraph-checkpoint': 1.0.1(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))
|
'@langchain/langgraph-checkpoint': 1.0.1(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))
|
||||||
'@langchain/langgraph-sdk': 1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
'@langchain/langgraph-sdk': 1.8.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)
|
||||||
'@standard-schema/spec': 1.1.0
|
'@standard-schema/spec': 1.1.0
|
||||||
uuid: 10.0.0
|
uuid: 10.0.0
|
||||||
zod: 3.25.76
|
zod: 4.3.6
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
zod-to-json-schema: 3.25.2(zod@4.3.6)
|
zod-to-json-schema: 3.25.2(zod@4.3.6)
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
@@ -5379,13 +5455,6 @@ snapshots:
|
|||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
'@types/react': 19.2.14
|
'@types/react': 19.2.14
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.2.4(@types/react@19.2.14)(react@19.2.5)':
|
|
||||||
dependencies:
|
|
||||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.2.14)(react@19.2.5)
|
|
||||||
react: 19.2.5
|
|
||||||
optionalDependencies:
|
|
||||||
'@types/react': 19.2.14
|
|
||||||
|
|
||||||
'@radix-ui/react-slot@1.3.0(@types/react@19.2.14)(react@19.2.5)':
|
'@radix-ui/react-slot@1.3.0(@types/react@19.2.14)(react@19.2.5)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.5)
|
'@radix-ui/react-compose-refs': 1.1.3(@types/react@19.2.14)(react@19.2.5)
|
||||||
@@ -7186,10 +7255,10 @@ snapshots:
|
|||||||
langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)):
|
langchain@1.3.5(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(ws@8.20.0)(zod-to-json-schema@3.25.2(zod@4.3.6)):
|
||||||
dependencies:
|
dependencies:
|
||||||
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
'@langchain/core': 1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
'@langchain/langgraph': 1.2.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@3.25.76)
|
'@langchain/langgraph': 1.2.9(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))(react-dom@19.2.5(react@19.2.5))(react@19.2.5)(zod-to-json-schema@3.25.2(zod@4.3.6))(zod@4.3.6)
|
||||||
'@langchain/langgraph-checkpoint': 1.0.1(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))
|
'@langchain/langgraph-checkpoint': 1.0.1(@langchain/core@1.1.42(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0))
|
||||||
langsmith: 0.5.25(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
langsmith: 0.5.25(@opentelemetry/api@1.9.0)(openai@4.104.0(ws@8.20.0)(zod@4.3.6))(ws@8.20.0)
|
||||||
zod: 3.25.76
|
zod: 4.3.6
|
||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- '@opentelemetry/api'
|
- '@opentelemetry/api'
|
||||||
- '@opentelemetry/exporter-trace-otlp-proto'
|
- '@opentelemetry/exporter-trace-otlp-proto'
|
||||||
|
|||||||
@@ -120,6 +120,27 @@ class DeduplicateToolCallMiddleware extends (Middleware as any) {
|
|||||||
const open = new Set<string>(); // tool call IDs currently in progress
|
const open = new Set<string>(); // tool call IDs currently in progress
|
||||||
const closed = new Set<string>(); // tool call IDs that already received END
|
const closed = new Set<string>(); // tool call IDs that already received END
|
||||||
|
|
||||||
|
// Render tools are display-once: after the client returns their result,
|
||||||
|
// the model sometimes re-issues the same call (fresh id) on the
|
||||||
|
// follow-up run, painting a second identical card. Track which render
|
||||||
|
// tools already ran in the current user turn (from the request's own
|
||||||
|
// message history) and suppress repeats — both their streamed events
|
||||||
|
// and their entries in this run's MESSAGES_SNAPSHOT.
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const inputMessages: any[] = Array.isArray(input?.messages) ? input.messages : [];
|
||||||
|
let lastUserIdx = -1;
|
||||||
|
inputMessages.forEach((m, i) => { if (m?.role === "user") lastUserIdx = i; });
|
||||||
|
const renderedThisTurn = new Set<string>();
|
||||||
|
for (const m of inputMessages.slice(lastUserIdx + 1)) {
|
||||||
|
const calls = m?.toolCalls ?? m?.tool_calls;
|
||||||
|
if (m?.role !== "assistant" || !Array.isArray(calls)) continue;
|
||||||
|
for (const tc of calls) {
|
||||||
|
const name = tc?.function?.name;
|
||||||
|
if (name && RENDER_TOOLS.has(name)) renderedThisTurn.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const suppressed = new Set<string>(); // toolCallIds of dropped repeats
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return new Observable<any>((observer) => {
|
return new Observable<any>((observer) => {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
@@ -132,13 +153,51 @@ class DeduplicateToolCallMiddleware extends (Middleware as any) {
|
|||||||
|
|
||||||
if (type === EventType.TOOL_CALL_START) {
|
if (type === EventType.TOOL_CALL_START) {
|
||||||
if (!id || closed.has(id) || open.has(id)) return; // duplicate
|
if (!id || closed.has(id) || open.has(id)) return; // duplicate
|
||||||
|
const name: string | undefined = event?.toolCallName;
|
||||||
|
if (name && RENDER_TOOLS.has(name)) {
|
||||||
|
if (renderedThisTurn.has(name)) {
|
||||||
|
suppressed.add(id);
|
||||||
|
return; // repeat render call — drop the whole call
|
||||||
|
}
|
||||||
|
renderedThisTurn.add(name);
|
||||||
|
}
|
||||||
open.add(id);
|
open.add(id);
|
||||||
} else if (type === EventType.TOOL_CALL_ARGS || type === EventType.TOOL_CALL_END) {
|
} else if (type === EventType.TOOL_CALL_ARGS || type === EventType.TOOL_CALL_END) {
|
||||||
|
if (id && suppressed.has(id)) return;
|
||||||
if (id && closed.has(id)) return; // already completed, drop
|
if (id && closed.has(id)) return; // already completed, drop
|
||||||
if (type === EventType.TOOL_CALL_END && id) {
|
if (type === EventType.TOOL_CALL_END && id) {
|
||||||
open.delete(id);
|
open.delete(id);
|
||||||
closed.add(id);
|
closed.add(id);
|
||||||
}
|
}
|
||||||
|
} else if (type === EventType.MESSAGES_SNAPSHOT && suppressed.size > 0) {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const msgs: any[] = Array.isArray(event?.messages) ? event.messages : [];
|
||||||
|
const cleaned = msgs
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
.map((m: any) => {
|
||||||
|
const calls = m?.toolCalls ?? m?.tool_calls;
|
||||||
|
if (m?.role !== "assistant" || !Array.isArray(calls)) return m;
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const kept = calls.filter((tc: any) => !suppressed.has(String(tc?.id ?? "")));
|
||||||
|
if (kept.length === calls.length) return m;
|
||||||
|
const { toolCalls: _a, tool_calls: _b, ...rest } = m;
|
||||||
|
return kept.length > 0 ? { ...rest, toolCalls: kept } : rest;
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
.filter((m: any) => {
|
||||||
|
if (m?.role === "tool" && suppressed.has(String(m?.toolCallId ?? m?.tool_call_id ?? ""))) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (m?.role === "assistant") {
|
||||||
|
const calls = m?.toolCalls ?? m?.tool_calls;
|
||||||
|
const hasCalls = Array.isArray(calls) && calls.length > 0;
|
||||||
|
const hasText = typeof m?.content === "string" && m.content.length > 0;
|
||||||
|
if (!hasCalls && !hasText) return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
observer.next({ ...event, messages: cleaned });
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
observer.next(event); // emit raw BaseEvent
|
observer.next(event); // emit raw BaseEvent
|
||||||
@@ -192,23 +251,30 @@ class StripModelArtifactsMiddleware extends (Middleware as any) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// ── ReconcileSnapshotMiddleware ──────────────────────────────────────────────
|
// ── ReconcileSnapshotMiddleware ──────────────────────────────────────────────
|
||||||
// MAF's `_build_messages_snapshot` (agent_framework_ag_ui/_agent_run.py:686)
|
// MAF 1.10 reuses the streamed id for PLAIN-text snapshots, but still mints a
|
||||||
// mints a fresh UUID for the post-tool-call assistant text instead of reusing
|
// fresh UUID for post-tool-call assistant text — deliberately, per their
|
||||||
// the streamed TEXT_MESSAGE_START id. The resulting MESSAGES_SNAPSHOT then
|
// issue #3619 ("separate message"). ag-ui's snapshot merge appends unknown
|
||||||
// contains TWO assistant entries: the streamed id (holding the toolCalls) and
|
// ids, so tool+text turns still produce a duplicate assistant bubble without
|
||||||
// a brand-new id (holding the duplicated text). ag-ui's snapshot merge replaces
|
// this reconciliation (re-verified live on MAF 1.10 / CK 1.62, 2026-07-04).
|
||||||
// by id then APPENDS unknown ids, so the browser ends up with two assistant
|
// Drops only the orphan text-only assistant message with no streamed
|
||||||
// bubbles for the same answer. Dropping the snapshot entirely fixes the dupe
|
// counterpart. Caveat: interfered with a2ui retry runs (duplicated the user
|
||||||
// but breaks render_a2ui card persistence (cards rely on the snapshot to keep
|
// message) — a2ui is disabled while that pipeline matures; revisit both
|
||||||
// the assistant-with-toolCalls message in state past the run). The right fix
|
// together.
|
||||||
// is to drop just the orphan text-only assistant message that has no streamed
|
//
|
||||||
// counterpart. Remove once `_agent_run.py:686` reuses `flow.message_id`.
|
// Thread persistence (2026-07-04) adds two exemptions: a hydration replay
|
||||||
|
// (empty input.messages) is passed through untouched — nothing in it was
|
||||||
|
// streamed this run — and messages whose id the client already sent in
|
||||||
|
// input.messages are history, not duplicates.
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
class ReconcileSnapshotMiddleware extends (Middleware as any) {
|
class ReconcileSnapshotMiddleware extends (Middleware as any) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
run(input: any, next: any): Observable<any> {
|
run(input: any, next: any): Observable<any> {
|
||||||
const streamedTextIds = new Set<string>();
|
const streamedTextIds = new Set<string>();
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
const inputMessages: any[] = Array.isArray(input?.messages) ? input.messages : [];
|
||||||
|
const isHydration = inputMessages.length === 0;
|
||||||
|
const clientKnownIds = new Set<string>(inputMessages.map((m) => String(m?.id ?? "")));
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
return new Observable<any>((observer) => {
|
return new Observable<any>((observer) => {
|
||||||
@@ -223,7 +289,7 @@ class ReconcileSnapshotMiddleware extends (Middleware as any) {
|
|||||||
streamedTextIds.add(String(event.messageId));
|
streamedTextIds.add(String(event.messageId));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === EventType.MESSAGES_SNAPSHOT) {
|
if (type === EventType.MESSAGES_SNAPSHOT && !isHydration) {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
const msgs: any[] = Array.isArray(event?.messages) ? event.messages : [];
|
const msgs: any[] = Array.isArray(event?.messages) ? event.messages : [];
|
||||||
const filtered = msgs.filter((m) => {
|
const filtered = msgs.filter((m) => {
|
||||||
@@ -233,7 +299,12 @@ class ReconcileSnapshotMiddleware extends (Middleware as any) {
|
|||||||
const hasToolCalls =
|
const hasToolCalls =
|
||||||
(Array.isArray(m?.toolCalls) && m.toolCalls.length > 0) ||
|
(Array.isArray(m?.toolCalls) && m.toolCalls.length > 0) ||
|
||||||
(Array.isArray(m?.tool_calls) && m.tool_calls.length > 0);
|
(Array.isArray(m?.tool_calls) && m.tool_calls.length > 0);
|
||||||
if (hasText && !hasToolCalls && !streamedTextIds.has(id)) {
|
if (
|
||||||
|
hasText &&
|
||||||
|
!hasToolCalls &&
|
||||||
|
!streamedTextIds.has(id) &&
|
||||||
|
!clientKnownIds.has(id)
|
||||||
|
) {
|
||||||
return false; // drop orphan text-only assistant duplicate
|
return false; // drop orphan text-only assistant duplicate
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@@ -261,7 +332,7 @@ class ReconcileSnapshotMiddleware extends (Middleware as any) {
|
|||||||
// below the card. This middleware buffers TEXT_MESSAGE_* events and discards
|
// below the card. This middleware buffers TEXT_MESSAGE_* events and discards
|
||||||
// them if any render tool call is detected in the same turn.
|
// them if any render tool call is detected in the same turn.
|
||||||
|
|
||||||
const RENDER_TOOLS = new Set(["render_a2ui", "render_spending_summary"]);
|
const RENDER_TOOLS = new Set(["render_spending_summary"]);
|
||||||
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
class SuppressRenderToolTextMiddleware extends (Middleware as any) {
|
class SuppressRenderToolTextMiddleware extends (Middleware as any) {
|
||||||
@@ -356,38 +427,73 @@ app.all("/api/copilotkit/*", async (c) => {
|
|||||||
: {};
|
: {};
|
||||||
|
|
||||||
const agent = new HttpAgent({ url: AGENT_URL, headers: agentHeaders });
|
const agent = new HttpAgent({ url: AGENT_URL, headers: agentHeaders });
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// CK_MIDDLEWARES=off runs the stack bare — used to audit which of these
|
||||||
agent.use(new SuppressRenderToolTextMiddleware() as any);
|
// workarounds are still needed after CopilotKit/MAF upgrades.
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
agent.use(new StripModelArtifactsMiddleware() as any);
|
if (process.env.CK_MIDDLEWARES !== "off") {
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
agent.use(new SuppressRenderToolTextMiddleware() as any);
|
||||||
agent.use(new ReconcileSnapshotMiddleware() as any);
|
agent.use(new StripModelArtifactsMiddleware() as any);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
agent.use(new ReconcileSnapshotMiddleware() as any);
|
||||||
agent.use(new DeduplicateToolCallMiddleware() as any);
|
agent.use(new DeduplicateToolCallMiddleware() as any);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
}
|
||||||
agent.use(new DebugLogMiddleware() as any);
|
if (process.env.CK_DEBUG_EVENTS === "1" || process.env.CK_MIDDLEWARES === "off") {
|
||||||
|
agent.use(new DebugLogMiddleware() as any);
|
||||||
|
}
|
||||||
|
/* eslint-enable @typescript-eslint/no-explicit-any */
|
||||||
|
|
||||||
const runtime = new CopilotRuntime({
|
const runtime = new CopilotRuntime({
|
||||||
agents: { wealthysmart: agent },
|
agents: { wealthysmart: agent },
|
||||||
a2ui: { injectA2UITool: true },
|
// a2ui disabled 2026-07-04: CK 1.60+ validates generated ops against a
|
||||||
|
// catalog and our agent's ops fail validation (surface never paints).
|
||||||
|
// Markdown tables cover the use case until the pipeline matures.
|
||||||
|
a2ui: { enabled: false },
|
||||||
beforeRequestMiddleware: async ({ request: outbound }) => {
|
beforeRequestMiddleware: async ({ request: outbound }) => {
|
||||||
if (outbound.method !== "POST") return;
|
if (outbound.method !== "POST") return;
|
||||||
const ct = outbound.headers.get("content-type") ?? "";
|
const ct = outbound.headers.get("content-type") ?? "";
|
||||||
if (!ct.includes("application/json")) return;
|
if (!ct.includes("application/json")) return;
|
||||||
try {
|
try {
|
||||||
const body = (await outbound.clone().json()) as { messages?: AGUIMessage[] };
|
const body = (await outbound.clone().json()) as {
|
||||||
|
messages?: AGUIMessage[];
|
||||||
|
context?: { description?: string; value?: string }[];
|
||||||
|
};
|
||||||
if (!Array.isArray(body.messages)) return;
|
if (!Array.isArray(body.messages)) return;
|
||||||
const paired = pairOrphanToolCalls(body.messages);
|
// An empty messages array is MAF's hydration trigger (known threadId
|
||||||
if (paired.length === body.messages.length) return;
|
// + no messages → replay the stored snapshot without invoking the
|
||||||
|
// LLM). Injecting anything would turn it into a normal run.
|
||||||
|
if (body.messages.length === 0) return;
|
||||||
|
let messages = pairOrphanToolCalls(body.messages);
|
||||||
|
|
||||||
|
// MAF's AG-UI endpoint declares `context` on its request model but
|
||||||
|
// never feeds it to the model (agent_framework_ag_ui, checked at
|
||||||
|
// python-1.10.0). CopilotKit 1.60+ ships the A2UI catalog schema and
|
||||||
|
// guidelines in that context — without them the model emits ops the
|
||||||
|
// A2UI validator rejects and the surface never paints. Inject the
|
||||||
|
// context as a system message ourselves. Remove once MAF consumes
|
||||||
|
// RunAgentInput.context.
|
||||||
|
if (Array.isArray(body.context) && body.context.length > 0) {
|
||||||
|
const contextText = body.context
|
||||||
|
.map((c) => `## ${c.description ?? "Context"}\n${c.value ?? ""}`)
|
||||||
|
.join("\n\n");
|
||||||
|
messages = [
|
||||||
|
{
|
||||||
|
id: `ctx-${Date.now()}`,
|
||||||
|
role: "system",
|
||||||
|
content: `Additional run context:\n\n${contextText}`,
|
||||||
|
} as AGUIMessage,
|
||||||
|
...messages,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (messages === body.messages) return;
|
||||||
return new Request(outbound.url, {
|
return new Request(outbound.url, {
|
||||||
method: outbound.method,
|
method: outbound.method,
|
||||||
headers: outbound.headers,
|
headers: outbound.headers,
|
||||||
body: JSON.stringify({ ...body, messages: paired }),
|
body: JSON.stringify({ ...body, messages }),
|
||||||
});
|
});
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
// Skipping the repair is survivable; doing it silently is not —
|
// Skipping the repair is survivable; doing it silently is not —
|
||||||
// orphan tool calls then fail downstream with no trace of why.
|
// orphan tool calls then fail downstream with no trace of why.
|
||||||
console.error("[copilotkit] orphan-tool-call repair skipped:", err);
|
console.error("[copilotkit] outbound request repair skipped:", err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ export default function App() {
|
|||||||
<PrivacyProvider>
|
<PrivacyProvider>
|
||||||
<AuthProvider>
|
<AuthProvider>
|
||||||
<QueryClientProvider client={queryClient}>
|
<QueryClientProvider client={queryClient}>
|
||||||
<CopilotKit runtimeUrl="/api/copilotkit" agent="wealthysmart" a2ui={{}}>
|
<CopilotKit runtimeUrl="/api/copilotkit" agent="wealthysmart">
|
||||||
<AppRoutes />
|
<AppRoutes />
|
||||||
<Toaster richColors position="top-right" closeButton />
|
<Toaster richColors position="top-right" closeButton />
|
||||||
</CopilotKit>
|
</CopilotKit>
|
||||||
|
|||||||
@@ -27,11 +27,14 @@ export default function Layout() {
|
|||||||
const { privacyMode, togglePrivacy } = usePrivacy();
|
const { privacyMode, togglePrivacy } = usePrivacy();
|
||||||
const { pathname } = useLocation();
|
const { pathname } = useLocation();
|
||||||
const title = titleFor(pathname);
|
const title = titleFor(pathname);
|
||||||
|
// Asistente hosts its own scroll container (the chat), so its route caps
|
||||||
|
// the shell at the viewport; every other page keeps normal body scroll.
|
||||||
|
const fullHeight = pathname === "/asistente";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SidebarProvider>
|
<SidebarProvider>
|
||||||
<AppSidebar />
|
<AppSidebar />
|
||||||
<SidebarInset>
|
<SidebarInset className={fullHeight ? "h-svh overflow-hidden" : undefined}>
|
||||||
<header className="sticky top-0 z-40 flex h-14 shrink-0 items-center gap-2 border-b border-border bg-background/90 px-4 backdrop-blur-sm">
|
<header className="sticky top-0 z-40 flex h-14 shrink-0 items-center gap-2 border-b border-border bg-background/90 px-4 backdrop-blur-sm">
|
||||||
<SidebarTrigger className="-ml-1" />
|
<SidebarTrigger className="-ml-1" />
|
||||||
<Separator
|
<Separator
|
||||||
|
|||||||
@@ -345,6 +345,23 @@ export interface paths {
|
|||||||
patch: operations["update_category_api_v1_categories__category_id__patch"];
|
patch: operations["update_category_api_v1_categories__category_id__patch"];
|
||||||
trace?: never;
|
trace?: never;
|
||||||
};
|
};
|
||||||
|
"/api/v1/chat/thread": {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: never;
|
||||||
|
path?: never;
|
||||||
|
cookie?: never;
|
||||||
|
};
|
||||||
|
get?: never;
|
||||||
|
put?: never;
|
||||||
|
post?: never;
|
||||||
|
/** Clear Thread */
|
||||||
|
delete: operations["clear_thread_api_v1_chat_thread_delete"];
|
||||||
|
options?: never;
|
||||||
|
head?: never;
|
||||||
|
patch?: never;
|
||||||
|
trace?: never;
|
||||||
|
};
|
||||||
"/api/v1/exchange-rate/": {
|
"/api/v1/exchange-rate/": {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
@@ -1200,6 +1217,11 @@ export interface components {
|
|||||||
/** Name */
|
/** Name */
|
||||||
name?: string | null;
|
name?: string | null;
|
||||||
};
|
};
|
||||||
|
/** ClearThreadResponse */
|
||||||
|
ClearThreadResponse: {
|
||||||
|
/** Cleared */
|
||||||
|
cleared: boolean;
|
||||||
|
};
|
||||||
/**
|
/**
|
||||||
* Currency
|
* Currency
|
||||||
* @enum {string}
|
* @enum {string}
|
||||||
@@ -3023,6 +3045,39 @@ export interface operations {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
clear_thread_api_v1_chat_thread_delete: {
|
||||||
|
parameters: {
|
||||||
|
query?: never;
|
||||||
|
header?: {
|
||||||
|
authorization?: string | null;
|
||||||
|
};
|
||||||
|
path?: never;
|
||||||
|
cookie?: {
|
||||||
|
ws_token?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
requestBody?: never;
|
||||||
|
responses: {
|
||||||
|
/** @description Successful Response */
|
||||||
|
200: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["ClearThreadResponse"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
/** @description Validation Error */
|
||||||
|
422: {
|
||||||
|
headers: {
|
||||||
|
[name: string]: unknown;
|
||||||
|
};
|
||||||
|
content: {
|
||||||
|
"application/json": components["schemas"]["HTTPValidationError"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
current_rate_api_v1_exchange_rate__get: {
|
current_rate_api_v1_exchange_rate__get: {
|
||||||
parameters: {
|
parameters: {
|
||||||
query?: never;
|
query?: never;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { useEffect, useRef } from "react";
|
import { useEffect, useRef, useState } from "react";
|
||||||
import { useLocation, useNavigate } from "react-router-dom";
|
import { useLocation, useNavigate } from "react-router-dom";
|
||||||
import {
|
import {
|
||||||
CopilotChat,
|
CopilotChat,
|
||||||
@@ -7,10 +7,17 @@ import {
|
|||||||
useCopilotKit,
|
useCopilotKit,
|
||||||
} from "@copilotkit/react-core/v2";
|
} from "@copilotkit/react-core/v2";
|
||||||
import { useCopilotAction } from "@copilotkit/react-core";
|
import { useCopilotAction } from "@copilotkit/react-core";
|
||||||
import { Sparkles } from "lucide-react";
|
import { MessageSquarePlus, Sparkles } from "lucide-react";
|
||||||
|
import { Button } from "@/components/ui/button";
|
||||||
|
import ConfirmDialog from "@/components/ConfirmDialog";
|
||||||
import { PageHeader } from '@/components/page-header';
|
import { PageHeader } from '@/components/page-header';
|
||||||
import { SpendingSummaryCard, type SpendingSummaryArgs } from "@/components/chat/ChatCards";
|
import { SpendingSummaryCard, type SpendingSummaryArgs } from "@/components/chat/ChatCards";
|
||||||
|
|
||||||
|
// Single persistent conversation: the backend snapshot store keys on this
|
||||||
|
// same id (backend/app/api/v1/endpoints/chat.py). An explicit, stable
|
||||||
|
// threadId also stops CopilotChat from wiping messages on remount.
|
||||||
|
const CHAT_THREAD_ID = "main";
|
||||||
|
|
||||||
const STATIC_SUGGESTIONS = {
|
const STATIC_SUGGESTIONS = {
|
||||||
available: "before-first-message" as const,
|
available: "before-first-message" as const,
|
||||||
suggestions: [
|
suggestions: [
|
||||||
@@ -24,28 +31,66 @@ const STATIC_SUGGESTIONS = {
|
|||||||
export default function Asistente() {
|
export default function Asistente() {
|
||||||
useConfigureSuggestions(STATIC_SUGGESTIONS);
|
useConfigureSuggestions(STATIC_SUGGESTIONS);
|
||||||
|
|
||||||
// A question typed on the Inicio dashboard arrives via router state:
|
|
||||||
// append it as a user message and run the agent once.
|
|
||||||
const location = useLocation();
|
const location = useLocation();
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { agent } = useAgent({ agentId: "wealthysmart" });
|
const { agent } = useAgent({ agentId: "wealthysmart" });
|
||||||
const { copilotkit } = useCopilotKit();
|
const { copilotkit } = useCopilotKit();
|
||||||
const sentRef = useRef(false);
|
const [confirmClear, setConfirmClear] = useState(false);
|
||||||
|
const [clearing, setClearing] = useState(false);
|
||||||
|
|
||||||
|
// Boot sequence, once per page load: hydrate the persisted thread, then
|
||||||
|
// send any question that arrived from the Inicio ask-box via router state.
|
||||||
|
// Order matters — hydration first keeps the invariant that every normal
|
||||||
|
// run's input carries the full client-known history (the BFF's snapshot
|
||||||
|
// reconciliation relies on it).
|
||||||
|
const bootRef = useRef(false);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
if (!agent || bootRef.current) return;
|
||||||
|
bootRef.current = true;
|
||||||
const ask = (location.state as { ask?: string } | null)?.ask;
|
const ask = (location.state as { ask?: string } | null)?.ask;
|
||||||
if (!ask || sentRef.current || !agent) return;
|
if (ask) {
|
||||||
sentRef.current = true;
|
// Clear the state so back/refresh doesn't re-send the question.
|
||||||
// Clear the state so back/refresh doesn't re-send the question.
|
navigate(location.pathname, { replace: true, state: null });
|
||||||
navigate(location.pathname, { replace: true, state: null });
|
}
|
||||||
agent.addMessage({
|
void (async () => {
|
||||||
id: crypto.randomUUID(),
|
agent.threadId = CHAT_THREAD_ID;
|
||||||
role: "user",
|
if (agent.messages.length === 0) {
|
||||||
content: ask,
|
// Empty-messages run = MAF's hydration trigger: replays the stored
|
||||||
});
|
// snapshot as MESSAGES_SNAPSHOT without invoking the LLM.
|
||||||
void copilotkit.runAgent({ agent });
|
try {
|
||||||
|
await copilotkit.runAgent({ agent });
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[asistente] thread hydration failed:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (ask) {
|
||||||
|
agent.addMessage({
|
||||||
|
id: crypto.randomUUID(),
|
||||||
|
role: "user",
|
||||||
|
content: ask,
|
||||||
|
});
|
||||||
|
void copilotkit.runAgent({ agent });
|
||||||
|
}
|
||||||
|
})();
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
}, [agent]);
|
}, [agent]);
|
||||||
|
|
||||||
|
const clearThread = async () => {
|
||||||
|
setClearing(true);
|
||||||
|
try {
|
||||||
|
await fetch("/api/v1/chat/thread", {
|
||||||
|
method: "DELETE",
|
||||||
|
credentials: "include",
|
||||||
|
});
|
||||||
|
agent?.setMessages([]);
|
||||||
|
setConfirmClear(false);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("[asistente] clear thread failed:", err);
|
||||||
|
} finally {
|
||||||
|
setClearing(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
useCopilotAction({
|
useCopilotAction({
|
||||||
name: "render_spending_summary",
|
name: "render_spending_summary",
|
||||||
description:
|
description:
|
||||||
@@ -92,12 +137,35 @@ export default function Asistente() {
|
|||||||
icon={Sparkles}
|
icon={Sparkles}
|
||||||
title="Asistente"
|
title="Asistente"
|
||||||
subtitle="Pregúntale a WealthySmart sobre tus finanzas."
|
subtitle="Pregúntale a WealthySmart sobre tus finanzas."
|
||||||
|
actions={
|
||||||
|
<Button
|
||||||
|
variant="outline"
|
||||||
|
size="sm"
|
||||||
|
onClick={() => setConfirmClear(true)}
|
||||||
|
disabled={!agent || agent.messages.length === 0}
|
||||||
|
>
|
||||||
|
<MessageSquarePlus />
|
||||||
|
Nueva conversación
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{confirmClear && (
|
||||||
|
<ConfirmDialog
|
||||||
|
title="¿Nueva conversación?"
|
||||||
|
message="Se borrará el historial del chat guardado. Esta acción no se puede deshacer."
|
||||||
|
confirmLabel="Borrar historial"
|
||||||
|
onConfirm={clearThread}
|
||||||
|
onCancel={() => setConfirmClear(false)}
|
||||||
|
loading={clearing}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex-1 min-h-0 rounded-xl border border-border overflow-hidden bg-card">
|
<div className="flex-1 min-h-0 rounded-xl border border-border overflow-hidden bg-card">
|
||||||
<CopilotChat
|
<CopilotChat
|
||||||
className="h-full"
|
className="h-full"
|
||||||
|
threadId={CHAT_THREAD_ID}
|
||||||
labels={{
|
labels={{
|
||||||
modalHeaderTitle: "WealthySmart",
|
modalHeaderTitle: "WealthySmart",
|
||||||
welcomeMessageText: "¿Qué quieres saber sobre tus finanzas?",
|
welcomeMessageText: "¿Qué quieres saber sobre tus finanzas?",
|
||||||
|
|||||||
Reference in New Issue
Block a user