mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-06 16:01:01 +00:00
fix(mattermost): accept streaming config
This commit is contained in:
@@ -78,6 +78,40 @@ const MattermostNetworkSchema = z
|
||||
.strict()
|
||||
.optional();
|
||||
|
||||
const MattermostStreamingModeSchema = z.enum(["off", "partial", "block", "progress"]);
|
||||
const MattermostStreamingProgressSchema = z
|
||||
.object({
|
||||
label: z.union([z.string(), z.literal(false)]).optional(),
|
||||
labels: z.array(z.string()).optional(),
|
||||
maxLines: z.number().int().positive().optional(),
|
||||
toolProgress: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
const MattermostStreamingPreviewSchema = z
|
||||
.object({
|
||||
toolProgress: z.boolean().optional(),
|
||||
})
|
||||
.strict();
|
||||
const MattermostStreamingBlockSchema = z
|
||||
.object({
|
||||
enabled: z.boolean().optional(),
|
||||
coalesce: BlockStreamingCoalesceSchema.optional(),
|
||||
})
|
||||
.strict();
|
||||
const MattermostStreamingSchema = z.union([
|
||||
MattermostStreamingModeSchema,
|
||||
z.boolean(),
|
||||
z
|
||||
.object({
|
||||
mode: MattermostStreamingModeSchema.optional(),
|
||||
chunkMode: z.enum(["length", "newline"]).optional(),
|
||||
preview: MattermostStreamingPreviewSchema.optional(),
|
||||
progress: MattermostStreamingProgressSchema.optional(),
|
||||
block: MattermostStreamingBlockSchema.optional(),
|
||||
})
|
||||
.strict(),
|
||||
]);
|
||||
|
||||
const MattermostAccountSchemaBase = z
|
||||
.object({
|
||||
name: z.string().optional(),
|
||||
@@ -97,6 +131,7 @@ const MattermostAccountSchemaBase = z
|
||||
groupPolicy: GroupPolicySchema.optional().default("allowlist"),
|
||||
textChunkLimit: z.number().int().positive().optional(),
|
||||
chunkMode: z.enum(["length", "newline"]).optional(),
|
||||
streaming: MattermostStreamingSchema.optional(),
|
||||
blockStreaming: z.boolean().optional(),
|
||||
blockStreamingCoalesce: BlockStreamingCoalesceSchema.optional(),
|
||||
replyToMode: z.enum(["off", "first", "all", "batched"]).optional(),
|
||||
|
||||
@@ -29,6 +29,25 @@ describe("MattermostConfigSchema", () => {
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts documented streaming modes and progress config", () => {
|
||||
const result = MattermostConfigSchema.safeParse({
|
||||
streaming: {
|
||||
mode: "progress",
|
||||
progress: {
|
||||
label: "Shelling",
|
||||
maxLines: 4,
|
||||
toolProgress: false,
|
||||
},
|
||||
},
|
||||
accounts: {
|
||||
quiet: {
|
||||
streaming: "off",
|
||||
},
|
||||
},
|
||||
});
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it("accepts groups with requireMention", () => {
|
||||
const result = MattermostConfigSchema.safeParse({
|
||||
groups: {
|
||||
|
||||
@@ -135,4 +135,24 @@ describe("resolveMattermostReplyToMode", () => {
|
||||
callbackPath: "/hooks/work",
|
||||
});
|
||||
});
|
||||
|
||||
it("resolves documented streaming mode from account config", () => {
|
||||
const account = resolveMattermostAccount({
|
||||
cfg: {
|
||||
channels: {
|
||||
mattermost: {
|
||||
streaming: "partial",
|
||||
accounts: {
|
||||
work: {
|
||||
streaming: "off",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
accountId: "work",
|
||||
});
|
||||
|
||||
expect(account.streamingMode).toBe("off");
|
||||
});
|
||||
});
|
||||
|
||||
@@ -5,6 +5,8 @@ import {
|
||||
resolveChannelStreamingBlockCoalesce,
|
||||
resolveChannelStreamingBlockEnabled,
|
||||
resolveChannelStreamingChunkMode,
|
||||
resolveChannelPreviewStreamMode,
|
||||
type StreamingMode,
|
||||
} from "openclaw/plugin-sdk/channel-streaming";
|
||||
import { normalizeOptionalString } from "openclaw/plugin-sdk/text-runtime";
|
||||
import { normalizeResolvedSecretInputString, normalizeSecretInputString } from "../secret-input.js";
|
||||
@@ -34,6 +36,7 @@ export type ResolvedMattermostAccount = {
|
||||
requireMention?: boolean;
|
||||
textChunkLimit?: number;
|
||||
chunkMode?: MattermostAccountConfig["chunkMode"];
|
||||
streamingMode: StreamingMode;
|
||||
blockStreaming?: boolean;
|
||||
blockStreamingCoalesce?: MattermostAccountConfig["blockStreamingCoalesce"];
|
||||
};
|
||||
@@ -120,6 +123,7 @@ export function resolveMattermostAccount(params: {
|
||||
requireMention,
|
||||
textChunkLimit: merged.textChunkLimit,
|
||||
chunkMode: resolveChannelStreamingChunkMode(merged) ?? merged.chunkMode,
|
||||
streamingMode: resolveChannelPreviewStreamMode(merged, "partial"),
|
||||
blockStreaming: resolveChannelStreamingBlockEnabled(merged) ?? merged.blockStreaming,
|
||||
blockStreamingCoalesce:
|
||||
resolveChannelStreamingBlockCoalesce(merged) ?? merged.blockStreamingCoalesce,
|
||||
|
||||
@@ -13,6 +13,7 @@ const accountFixture: ResolvedMattermostAccount = {
|
||||
baseUrl: "https://chat.example.com",
|
||||
botTokenSource: "config",
|
||||
baseUrlSource: "config",
|
||||
streamingMode: "partial",
|
||||
config: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -281,6 +281,20 @@ type MattermostDraftPreviewState = {
|
||||
finalizedViaPreviewPost: boolean;
|
||||
};
|
||||
|
||||
function createDisabledMattermostDraftStream(): ReturnType<typeof createMattermostDraftStream> {
|
||||
const noopAsync = async () => {};
|
||||
return {
|
||||
update: () => {},
|
||||
flush: noopAsync,
|
||||
postId: () => undefined,
|
||||
clear: noopAsync,
|
||||
discardPending: noopAsync,
|
||||
seal: noopAsync,
|
||||
stop: noopAsync,
|
||||
forceNewMessage: () => {},
|
||||
};
|
||||
}
|
||||
|
||||
type MattermostDraftPreviewDeliverParams = {
|
||||
payload: ReplyPayload;
|
||||
info: { kind: "tool" | "block" | "final" };
|
||||
@@ -1619,14 +1633,17 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
||||
},
|
||||
},
|
||||
});
|
||||
const draftStream = createMattermostDraftStream({
|
||||
client,
|
||||
channelId,
|
||||
rootId: effectiveReplyToId,
|
||||
throttleMs: 1200,
|
||||
log: logVerboseMessage,
|
||||
warn: logVerboseMessage,
|
||||
});
|
||||
const draftPreviewEnabled = account.streamingMode !== "off";
|
||||
const draftStream = draftPreviewEnabled
|
||||
? createMattermostDraftStream({
|
||||
client,
|
||||
channelId,
|
||||
rootId: effectiveReplyToId,
|
||||
throttleMs: 1200,
|
||||
log: logVerboseMessage,
|
||||
warn: logVerboseMessage,
|
||||
})
|
||||
: createDisabledMattermostDraftStream();
|
||||
let lastPartialText = "";
|
||||
const previewState: MattermostDraftPreviewState = {
|
||||
finalizedViaPreviewPost: false,
|
||||
@@ -1815,7 +1832,9 @@ export async function monitorMattermostProvider(opts: MonitorMattermostOpts = {}
|
||||
disableBlockStreaming: true,
|
||||
onModelSelected,
|
||||
onPartialReply: (payload) => {
|
||||
updateDraftFromPartial(payload.text);
|
||||
if (account.streamingMode !== "progress") {
|
||||
updateDraftFromPartial(payload.text);
|
||||
}
|
||||
},
|
||||
onAssistantMessageStart: () => {
|
||||
lastPartialText = "";
|
||||
|
||||
@@ -205,6 +205,7 @@ const accountFixture: ResolvedMattermostAccount = {
|
||||
baseUrl: "https://chat.example.com",
|
||||
botTokenSource: "config",
|
||||
baseUrlSource: "config",
|
||||
streamingMode: "partial",
|
||||
config: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -68,6 +68,7 @@ const accountFixture: ResolvedMattermostAccount = {
|
||||
baseUrl: "https://chat.example.com",
|
||||
botTokenSource: "config",
|
||||
baseUrlSource: "config",
|
||||
streamingMode: "partial",
|
||||
config: {},
|
||||
};
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ function createResolvedMattermostAccount(accountId: string): ResolvedMattermostA
|
||||
enabled: true,
|
||||
botTokenSource: "config",
|
||||
baseUrlSource: "config",
|
||||
streamingMode: "partial",
|
||||
config: {},
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,3 +1,7 @@
|
||||
import type {
|
||||
ChannelPreviewStreamingConfig,
|
||||
StreamingMode,
|
||||
} from "openclaw/plugin-sdk/channel-streaming";
|
||||
import type { BlockStreamingCoalesceConfig, DmPolicy, GroupPolicy } from "./runtime-api.js";
|
||||
import type { SecretInput } from "./secret-input.js";
|
||||
|
||||
@@ -51,6 +55,8 @@ export type MattermostAccountConfig = {
|
||||
textChunkLimit?: number;
|
||||
/** Chunking mode: "length" (default) splits by size; "newline" splits on every newline. */
|
||||
chunkMode?: "length" | "newline";
|
||||
/** Preview streaming mode/config. */
|
||||
streaming?: StreamingMode | boolean | ChannelPreviewStreamingConfig;
|
||||
/** Disable block streaming for this account. */
|
||||
blockStreaming?: boolean;
|
||||
/** Merge streamed block replies before sending. */
|
||||
|
||||
Reference in New Issue
Block a user