Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .yarnrc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ npmPreapprovedPackages:
- stream-chat
- react-native-teleport
- react-native-nitro-sound
- '@stream-io/calypso'
- '@stream-io/stream-agents'
- '@a2ui/web_core'

npmPublishProvenance: true
Expand Down
14 changes: 7 additions & 7 deletions examples/ExpoChatGPTSample/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,18 @@ Ported from the `ExpoChatGPTSample` of the deprecated `@stream-io/chat-react-nat
## How it works

The agent runs on a [Vision-Agents](https://github.com/GetStream/Vision-Agents) router, which the app talks to through
[`@stream-io/calypso`](https://www.npmjs.com/package/@stream-io/calypso) (`agent/`).
[`@stream-io/stream-agents`](https://www.npmjs.com/package/@stream-io/stream-agents) (`agent/`).

- Each conversation is a router session, kept in an `agent` channel. The drawer lists the user's `agent` channels. `+`
starts a new session, and one that is never asked anything is deleted when you leave it.
- The conversation is `AIConversation`, fed from `agent/agentChat.ts`: a small store that watches the channel, copies
its messages on every `message.*` event, loads older pages, and keeps each reply's full reasoning from its live
updates. There's no `<Channel>`, so the store also watches the channel again after `connection.recovered`, to pick up
what was written while the connection was down.
- The conversation is `AIConversation`, over an `AIConversationController` that `contexts/AppContext.tsx` builds from
the SDK's `AgentChat` and `agentChatTransport` (`@stream-io/stream-agents/chat`). `AgentChat` watches the channel,
loads older pages, keeps each reply's full reasoning from its live updates, and follows what the agent is doing on
the session's socket. The controller offers no edit or regenerate: the router can't rewind a conversation.
- Asking goes through the session, not the chat composer. The router writes the question and the streamed reply into
the channel, the reply's steps as `ai_reasoning` and `ai_tool_call` attachments, and titles the conversation itself.
A plain question goes to `respond` with this install's id, so the agent can call the phone's tools
(`agent/deviceTools.ts`). A question with photos or "Think harder" goes to `responses.create`, which takes neither.
Photos go as data URIs, and "Think harder" as a high reasoning effort. The phone's tools (`agent/deviceTools.ts`) are
declared on the session, which runs them.
- The keyboard is handled by the AI components through `react-native-keyboard-controller`; the app only wraps itself
in `KeyboardProvider`. The starters and the voice button sit in the composer's `header`, so they ride the keyboard.

Expand Down
180 changes: 0 additions & 180 deletions examples/ExpoChatGPTSample/agent/agentChat.ts

This file was deleted.

109 changes: 0 additions & 109 deletions examples/ExpoChatGPTSample/agent/agentTransport.ts

This file was deleted.

4 changes: 2 additions & 2 deletions examples/ExpoChatGPTSample/agent/agents.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { Client } from '@stream-io/calypso';
import { Client } from '@stream-io/stream-agents';
// Lets `session.chat()` open the conversation's channel on the app's Stream Chat client.
import '@stream-io/calypso/chat';
import '@stream-io/stream-agents/chat';

import { AgentConfig } from './config';

Expand Down
49 changes: 49 additions & 0 deletions examples/ExpoChatGPTSample/agent/conversation.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import type { AgentChat, AgentChatState } from '@stream-io/stream-agents/chat';
import type { LocalMessage, StateStore } from 'stream-chat';

/** A written conversation with the agent, over the messages of the channel it is kept in. */
export type Chat = AgentChat<LocalMessage>;

export type ChatState = AgentChatState<LocalMessage>;

/**
* The chat's state for `useStateStore`. The SDK's store has the shape of stream-chat's
* `StateStore` (`getLatestValue` and `subscribeWithSelector`, which is all `useStateStore` calls)
* without depending on stream-chat, so the type is only told so here.
*/
export const chatStore = (chat: Chat) => chat.state as unknown as StateStore<ChatState>;

let keys = 0;
const chatKeys = new WeakMap<Chat, string>();

/** Tells a conversation apart from the one shown before it, for a React key. */
export const keyOf = (chat: Chat) => {
let key = chatKeys.get(chat);
if (!key) {
key = `chat-${++keys}`;
chatKeys.set(chat, key);
}
return key;
};

/** A reply the agent wrote. */
export const isAgentReply = (message: LocalMessage) => message.ai_generated === true;

/**
* Calls `used` when a new conversation is used, such as asked something: the router lists it from
* then on. A first question that never reached the router has its session deleted, and the next
* one creates another, which is listed in its turn. A conversation carried on is listed already,
* and isn't waited for. Returns a function that stops waiting.
*/
export const whenUsed = (chat: Chat, used: () => void) => {
if (chat.id !== undefined) return () => {};
let listed: string | undefined;
return chat.state.subscribeWithSelector(
({ usedSessionId }) => ({ usedSessionId }),
({ usedSessionId }) => {
if (usedSessionId === undefined || usedSessionId === listed) return;
listed = usedSessionId;
used();
},
);
};
Loading
Loading