@segmentstream/agent 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -7
- package/dist/extension/config.d.ts +24 -0
- package/dist/extension/config.mjs +59 -1
- package/dist/extension/extension.d.ts +3 -3
- package/dist/extension/extension.mjs +1 -1
- package/dist/extension/model.d.ts +6 -13
- package/dist/extension/model.mjs +8 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -51,17 +51,15 @@ import { segmentstream } from "@segmentstream/agent";
|
|
|
51
51
|
|
|
52
52
|
export default defineAgent({
|
|
53
53
|
defaultTools: false,
|
|
54
|
-
model: segmentstream.model(
|
|
55
|
-
default: "spacexai/grok-4.7",
|
|
56
|
-
byRole: { finance: "anthropic/claude-opus-5.5" },
|
|
57
|
-
preview: "google/gemini-flash",
|
|
58
|
-
}),
|
|
54
|
+
model: segmentstream.model(),
|
|
59
55
|
reasoning: "medium",
|
|
60
56
|
limits: { maxTokenCostUsdPerSession: 2 },
|
|
61
57
|
});
|
|
62
58
|
```
|
|
63
59
|
|
|
64
|
-
`segmentstream.model()` resolves on `session.started
|
|
60
|
+
`segmentstream.model()` resolves on `session.started` from `models.chat` in `agent/segmentstream.json`
|
|
61
|
+
(`{ "default", "byRole", "preview" }`), where the platform reads it too; an inline
|
|
62
|
+
`segmentstream.model({ default, byRole, preview })` works the same. On a preview deployment it returns `preview`
|
|
65
63
|
when set. Otherwise it returns the model of the first `byRole` entry whose role the member holds, in
|
|
66
64
|
the order written, and `default` when none matches. The roles come from the member's pairing, not
|
|
67
65
|
from the message. The model is never switched mid-session, which keeps the provider's prompt cache.
|
|
@@ -118,7 +116,8 @@ share a group chat's session never share a client.
|
|
|
118
116
|
proxy. The token stays in SegmentStream.
|
|
119
117
|
- **Webhook.** A webhook is accepted only with `X-Telegram-Bot-Api-Secret-Token` equal to
|
|
120
118
|
`SEGMENTSTREAM_AGENT_WEBHOOK_SECRET`, compared in constant time. The platform generates the
|
|
121
|
-
secret and registers the webhook after each production deployment
|
|
119
|
+
secret and registers the webhook after each production deployment, and sets the bot's menu
|
|
120
|
+
button, shown in private chats, to open the workspace app.
|
|
122
121
|
- **Production only.** Outside `VERCEL_ENV=production` the route answers 200 and does nothing, so a
|
|
123
122
|
preview never answers a real chat. Without `channels.telegram` it answers 404.
|
|
124
123
|
- **Pairing.** Messages from bots and channel posts are dropped. The sender's Telegram account is
|
|
@@ -30,6 +30,16 @@ export interface SegmentStreamAgentConfigInput {
|
|
|
30
30
|
};
|
|
31
31
|
};
|
|
32
32
|
readonly models?: {
|
|
33
|
+
/**
|
|
34
|
+
* The chat model `segmentstream.model()` chooses per session: `default` for everyone, one per
|
|
35
|
+
* member role in `byRole` (the first role the member holds wins, in the order written) and
|
|
36
|
+
* `preview` for preview deployments.
|
|
37
|
+
*/
|
|
38
|
+
readonly chat?: {
|
|
39
|
+
readonly default: string;
|
|
40
|
+
readonly byRole?: Readonly<Record<string, string>>;
|
|
41
|
+
readonly preview?: string;
|
|
42
|
+
};
|
|
33
43
|
/** A Gateway transcription model, or `false` to leave voice messages untranscribed. */
|
|
34
44
|
readonly stt?: string | boolean;
|
|
35
45
|
/** The Gateway decision model of the inbound router. */
|
|
@@ -44,12 +54,26 @@ export interface TelegramSettings {
|
|
|
44
54
|
readonly connection: string;
|
|
45
55
|
readonly policy: TelegramPolicy;
|
|
46
56
|
}
|
|
57
|
+
/** The chat models `segmentstream.model()` chooses between, as AI Gateway model ids. */
|
|
58
|
+
export interface ChatModelChoice {
|
|
59
|
+
/** The model for every session no other rule picks. */
|
|
60
|
+
readonly default: string;
|
|
61
|
+
/**
|
|
62
|
+
* A model per member role. The first entry whose role the member holds wins, in the order
|
|
63
|
+
* written, so put the more specific roles first.
|
|
64
|
+
*/
|
|
65
|
+
readonly byRole?: Readonly<Record<string, string>>;
|
|
66
|
+
/** The model of preview deployments, so a preview never spends on a large model. */
|
|
67
|
+
readonly preview?: string;
|
|
68
|
+
}
|
|
47
69
|
/** The validated configuration with defaults applied. It stays JSON-serializable. */
|
|
48
70
|
export interface SegmentStreamAgentConfig {
|
|
49
71
|
readonly channels: {
|
|
50
72
|
readonly telegram: TelegramSettings | null;
|
|
51
73
|
};
|
|
52
74
|
readonly models: {
|
|
75
|
+
/** `null` when `agent.ts` passes the choice to `segmentstream.model()` itself. */
|
|
76
|
+
readonly chat: ChatModelChoice | null;
|
|
53
77
|
readonly stt: string | false;
|
|
54
78
|
readonly decision: string;
|
|
55
79
|
};
|
|
@@ -72,6 +72,58 @@ function modelId(input) {
|
|
|
72
72
|
}
|
|
73
73
|
return input.value;
|
|
74
74
|
}
|
|
75
|
+
function chatModelChoice(value, issues) {
|
|
76
|
+
const path = ["models", "chat"];
|
|
77
|
+
const chat = section({
|
|
78
|
+
value,
|
|
79
|
+
path,
|
|
80
|
+
allowed: [
|
|
81
|
+
"default",
|
|
82
|
+
"byRole",
|
|
83
|
+
"preview"
|
|
84
|
+
],
|
|
85
|
+
issues
|
|
86
|
+
});
|
|
87
|
+
if (chat === void 0) return null;
|
|
88
|
+
const fallback = "";
|
|
89
|
+
if (chat.default === void 0) issues.push({
|
|
90
|
+
message: "is required",
|
|
91
|
+
path: [...path, "default"]
|
|
92
|
+
});
|
|
93
|
+
const defaultModel = modelId({
|
|
94
|
+
value: chat.default,
|
|
95
|
+
fallback,
|
|
96
|
+
path: [...path, "default"],
|
|
97
|
+
issues
|
|
98
|
+
});
|
|
99
|
+
const byRole = section({
|
|
100
|
+
value: chat.byRole,
|
|
101
|
+
path: [...path, "byRole"],
|
|
102
|
+
allowed: Object.keys(isRecord(chat.byRole) ? chat.byRole : {}),
|
|
103
|
+
issues
|
|
104
|
+
});
|
|
105
|
+
const roles = Object.fromEntries(Object.entries(byRole ?? {}).map(([role, model]) => [role, modelId({
|
|
106
|
+
value: model,
|
|
107
|
+
fallback,
|
|
108
|
+
path: [
|
|
109
|
+
...path,
|
|
110
|
+
"byRole",
|
|
111
|
+
role
|
|
112
|
+
],
|
|
113
|
+
issues
|
|
114
|
+
})]));
|
|
115
|
+
const preview = chat.preview === void 0 ? void 0 : modelId({
|
|
116
|
+
value: chat.preview,
|
|
117
|
+
fallback,
|
|
118
|
+
path: [...path, "preview"],
|
|
119
|
+
issues
|
|
120
|
+
});
|
|
121
|
+
return {
|
|
122
|
+
default: defaultModel,
|
|
123
|
+
...byRole === void 0 ? {} : { byRole: roles },
|
|
124
|
+
...preview === void 0 ? {} : { preview }
|
|
125
|
+
};
|
|
126
|
+
}
|
|
75
127
|
function validateConfig(input) {
|
|
76
128
|
const issues = [];
|
|
77
129
|
const root = section({
|
|
@@ -93,7 +145,11 @@ function validateConfig(input) {
|
|
|
93
145
|
const models = section({
|
|
94
146
|
value: root?.models,
|
|
95
147
|
path: ["models"],
|
|
96
|
-
allowed: [
|
|
148
|
+
allowed: [
|
|
149
|
+
"chat",
|
|
150
|
+
"stt",
|
|
151
|
+
"decision"
|
|
152
|
+
],
|
|
97
153
|
issues
|
|
98
154
|
});
|
|
99
155
|
const ontology = section({
|
|
@@ -103,6 +159,7 @@ function validateConfig(input) {
|
|
|
103
159
|
issues
|
|
104
160
|
});
|
|
105
161
|
const telegram = telegramSettings(channels?.telegram, issues);
|
|
162
|
+
const chat = chatModelChoice(models?.chat, issues);
|
|
106
163
|
const stt = models?.stt === false ? false : modelId({
|
|
107
164
|
value: models?.stt,
|
|
108
165
|
fallback: DEFAULT_STT_MODEL,
|
|
@@ -124,6 +181,7 @@ function validateConfig(input) {
|
|
|
124
181
|
return { value: {
|
|
125
182
|
channels: { telegram },
|
|
126
183
|
models: {
|
|
184
|
+
chat,
|
|
127
185
|
stt,
|
|
128
186
|
decision
|
|
129
187
|
},
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { manifestOf } from "./config.js";
|
|
2
|
-
import {
|
|
2
|
+
import { type DynamicModel, type ModelChoice } from "./model.js";
|
|
3
3
|
export declare const segmentstream: import("eve/extension").ExtensionHandle<{
|
|
4
4
|
readonly "~standard": {
|
|
5
5
|
readonly version: 1;
|
|
@@ -11,9 +11,9 @@ export declare const segmentstream: import("eve/extension").ExtensionHandle<{
|
|
|
11
11
|
};
|
|
12
12
|
};
|
|
13
13
|
}> & {
|
|
14
|
-
model:
|
|
14
|
+
model: (choice?: ModelChoice) => DynamicModel;
|
|
15
15
|
manifestOf: typeof manifestOf;
|
|
16
16
|
};
|
|
17
17
|
export default segmentstream;
|
|
18
|
-
export { DEFAULT_DECISION_MODEL, DEFAULT_STT_MODEL, SegmentStreamAgentConfigError, manifestOf, type SegmentStreamAgentConfig, type SegmentStreamAgentConfigInput, type TelegramPolicy, type TelegramSettings, } from "./config.js";
|
|
18
|
+
export { DEFAULT_DECISION_MODEL, DEFAULT_STT_MODEL, SegmentStreamAgentConfigError, manifestOf, type ChatModelChoice, type SegmentStreamAgentConfig, type SegmentStreamAgentConfigInput, type TelegramPolicy, type TelegramSettings, } from "./config.js";
|
|
19
19
|
export { chooseModel, model, type DynamicModel, type ModelChoice } from "./model.js";
|
|
@@ -9,7 +9,7 @@ import { chooseModel, model } from "./model.mjs";
|
|
|
9
9
|
import { defineExtension } from "eve/extension";
|
|
10
10
|
const extension = defineExtension({ config: configSchema });
|
|
11
11
|
const segmentstream = Object.assign(extension, {
|
|
12
|
-
model,
|
|
12
|
+
model: (choice) => model(choice ?? (() => extension.config.models.chat)),
|
|
13
13
|
manifestOf
|
|
14
14
|
});
|
|
15
15
|
export { DEFAULT_DECISION_MODEL, DEFAULT_STT_MODEL, SegmentStreamAgentConfigError, chooseModel, segmentstream as default, segmentstream, manifestOf, model };
|
|
@@ -1,18 +1,9 @@
|
|
|
1
1
|
import { type AgentModelDefinition, type AgentStaticModelDefinition } from "eve";
|
|
2
2
|
import type { SessionAuthContext } from "eve/context";
|
|
3
|
+
import type { ChatModelChoice } from "./config.js";
|
|
3
4
|
import { type Environment } from "./lib/environment.js";
|
|
4
5
|
/** The models `segmentstream.model()` chooses between, as AI Gateway model ids. */
|
|
5
|
-
export
|
|
6
|
-
/** The model for every session no other rule picks. */
|
|
7
|
-
readonly default: string;
|
|
8
|
-
/**
|
|
9
|
-
* A model per member role. The first entry whose role the member holds wins, in the order
|
|
10
|
-
* written, so put the more specific roles first.
|
|
11
|
-
*/
|
|
12
|
-
readonly byRole?: Readonly<Record<string, string>>;
|
|
13
|
-
/** The model of preview deployments, so a preview never spends on a large model. */
|
|
14
|
-
readonly preview?: string;
|
|
15
|
-
}
|
|
6
|
+
export type ModelChoice = ChatModelChoice;
|
|
16
7
|
/** The dynamic form of an agent's `model`, which `defineAgent` accepts as the whole field. */
|
|
17
8
|
export type DynamicModel = Exclude<AgentModelDefinition, AgentStaticModelDefinition>;
|
|
18
9
|
/** The one model for a session started by `principal`. */
|
|
@@ -24,6 +15,8 @@ export declare function chooseModel(input: {
|
|
|
24
15
|
/**
|
|
25
16
|
* The agent's model, chosen once when a session starts from the member's roles and `VERCEL_ENV`.
|
|
26
17
|
* Choosing once keeps the provider's prompt cache: a switch mid-session re-reads the history at
|
|
27
|
-
* full price.
|
|
18
|
+
* full price. The choice is given here, or read when the session starts, from `models.chat` of
|
|
19
|
+
* `agent/segmentstream.json` through the mounted extension; a session without either fails to
|
|
20
|
+
* start with a message naming the missing setting.
|
|
28
21
|
*/
|
|
29
|
-
export declare function model(choice: ModelChoice): DynamicModel;
|
|
22
|
+
export declare function model(choice: ModelChoice | (() => ModelChoice | null)): DynamicModel;
|
package/dist/extension/model.mjs
CHANGED
|
@@ -15,9 +15,13 @@ function chooseModel(input) {
|
|
|
15
15
|
return choice.default;
|
|
16
16
|
}
|
|
17
17
|
function model(choice) {
|
|
18
|
-
return defineDynamic({ events: { "session.started": (_event, context) =>
|
|
19
|
-
choice
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
return defineDynamic({ events: { "session.started": (_event, context) => {
|
|
19
|
+
const chosen = typeof choice === "function" ? choice() : choice;
|
|
20
|
+
if (chosen === null) throw new Error("segmentstream.model() has no model to choose from: set models.chat in agent/segmentstream.json or pass the choice to segmentstream.model().");
|
|
21
|
+
return chooseModel({
|
|
22
|
+
choice: chosen,
|
|
23
|
+
principal: context.session.auth.current ?? context.session.auth.initiator
|
|
24
|
+
});
|
|
25
|
+
} } });
|
|
22
26
|
}
|
|
23
27
|
export { chooseModel, model };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@segmentstream/agent",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Mount a SegmentStream workspace's agent on eve: the ontology as tools, acting as the member who writes, and Telegram through the workspace's connection.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"author": "SegmentStream",
|