@nocobase/portal-template-default 1.0.3 → 1.0.5

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.
Files changed (170) hide show
  1. package/README.MD +10 -5
  2. package/package.json +17 -17
  3. package/registry/nocobase-ai/components/chat/chat-message.tsx +8 -6
  4. package/registry/nocobase-ai/components/chat/chat-messages.tsx +8 -3
  5. package/registry/nocobase-ai/components/chat/markdown-message.tsx +26 -0
  6. package/registry/nocobase-ai/components/chat/sub-agent-conversation.tsx +3 -3
  7. package/registry/nocobase-ai/demo/configuration-gate.tsx +83 -35
  8. package/registry/nocobase-i18n/README.md +23 -32
  9. package/registry/nocobase-i18n/components/language-switcher.tsx +1 -1
  10. package/registry/nocobase-i18n/demo/index.tsx +2 -2
  11. package/registry/nocobase-i18n/demo/prompt-generator.tsx +3 -2
  12. package/registry/nocobase-i18n/extension.tsx +3 -4
  13. package/registry/nocobase-i18n/index.ts +4 -3
  14. package/registry/nocobase-i18n/locales/en-US.ts +2 -52
  15. package/registry/nocobase-i18n/locales/index.ts +8 -0
  16. package/registry/nocobase-i18n/locales/zh-CN.ts +2 -52
  17. package/registry/nocobase-i18n/provider.tsx +18 -6
  18. package/registry/nocobase-i18n/server-resources.ts +13 -31
  19. package/registry/nocobase-i18n/tests/i18n-regression.mjs +14 -5
  20. package/registry/nocobase-route-surfaces/README.md +21 -0
  21. package/registry/nocobase-route-surfaces/demo/index.tsx +266 -0
  22. package/{src/extensions/nocobase-acl → registry/nocobase-route-surfaces}/demo/lazy-route.tsx +2 -1
  23. package/registry/nocobase-route-surfaces/demo/prompt-generator.tsx +90 -0
  24. package/registry/nocobase-route-surfaces/demo/scenarios.ts +95 -0
  25. package/registry/nocobase-route-surfaces/extension.tsx +119 -0
  26. package/registry/nocobase-route-surfaces/index.ts +7 -0
  27. package/registry/nocobase-route-surfaces/route-dialog.tsx +145 -0
  28. package/registry/nocobase-route-surfaces/route-drawer.tsx +186 -0
  29. package/registry/nocobase-route-surfaces/route-page.tsx +29 -0
  30. package/registry/nocobase-route-surfaces/route-surface-context.ts +13 -0
  31. package/registry/nocobase-route-surfaces/use-refine-unsaved-changes.tsx +106 -0
  32. package/registry/nocobase-route-surfaces/use-route-surface-close.ts +15 -0
  33. package/registry/nocobase-route-surfaces/use-route-surface-state.ts +65 -0
  34. package/registry.config.json +30 -3
  35. package/scripts/registry.mjs +0 -2
  36. package/src/App.tsx +65 -35
  37. package/src/app/extension.ts +1 -2
  38. package/src/app/extensions.tsx +0 -14
  39. package/src/components/app-shell/breadcrumb.tsx +67 -2
  40. package/src/components/app-shell/sidebar.tsx +2 -2
  41. package/src/components/resources/views/list-view.tsx +3 -2
  42. package/src/components/ui/sheet.tsx +22 -21
  43. package/src/lib/nocobase/client.ts +6 -1
  44. package/src/locales/en-US.ts +81 -0
  45. package/src/locales/zh-CN.ts +77 -0
  46. package/src/pages/users/create.tsx +76 -53
  47. package/src/pages/users/edit.tsx +87 -56
  48. package/src/pages/users/index.ts +3 -0
  49. package/src/pages/users/layout.tsx +15 -0
  50. package/src/pages/users/list.tsx +19 -21
  51. package/src/pages/users/role-badges.tsx +43 -0
  52. package/src/pages/users/role-detail.tsx +272 -0
  53. package/src/pages/users/role-utils.ts +6 -0
  54. package/src/pages/users/routes.ts +24 -0
  55. package/src/pages/users/show.tsx +195 -104
  56. package/src/pages/users/types.ts +13 -0
  57. package/src/providers/i18n/index.ts +3 -0
  58. package/{registry/nocobase-i18n → src/providers/i18n}/runtime.ts +63 -16
  59. package/src/providers/system-settings/context.ts +26 -0
  60. package/src/providers/system-settings/index.ts +2 -0
  61. package/src/providers/system-settings/provider.tsx +94 -0
  62. package/src/extensions/nocobase-acl/README.md +0 -19
  63. package/src/extensions/nocobase-acl/components/acl-boundary.tsx +0 -106
  64. package/src/extensions/nocobase-acl/components/index.ts +0 -3
  65. package/src/extensions/nocobase-acl/components/role-options.ts +0 -36
  66. package/src/extensions/nocobase-acl/components/role-switcher.tsx +0 -135
  67. package/src/extensions/nocobase-acl/demo/boundary-api.tsx +0 -186
  68. package/src/extensions/nocobase-acl/demo/components.tsx +0 -231
  69. package/src/extensions/nocobase-acl/demo/index.tsx +0 -505
  70. package/src/extensions/nocobase-acl/demo/prompt-generator.tsx +0 -134
  71. package/src/extensions/nocobase-acl/demo/role-switcher-prompt-generator.tsx +0 -198
  72. package/src/extensions/nocobase-acl/demo/scenario-section.tsx +0 -37
  73. package/src/extensions/nocobase-acl/extension.tsx +0 -71
  74. package/src/extensions/nocobase-acl/index.ts +0 -1
  75. package/src/extensions/nocobase-ai/README.md +0 -70
  76. package/src/extensions/nocobase-ai/adapters/react-hook-form.ts +0 -20
  77. package/src/extensions/nocobase-ai/assets/nocobase-ai-chat.svg +0 -50
  78. package/src/extensions/nocobase-ai/components/ai-root-provider.tsx +0 -32
  79. package/src/extensions/nocobase-ai/components/chat/ai-employee-avatar.tsx +0 -31
  80. package/src/extensions/nocobase-ai/components/chat/chat-attachment.tsx +0 -92
  81. package/src/extensions/nocobase-ai/components/chat/chat-compact.tsx +0 -90
  82. package/src/extensions/nocobase-ai/components/chat/chat-composer.tsx +0 -429
  83. package/src/extensions/nocobase-ai/components/chat/chat-empty-state.tsx +0 -51
  84. package/src/extensions/nocobase-ai/components/chat/chat-header.tsx +0 -88
  85. package/src/extensions/nocobase-ai/components/chat/chat-history-dialog.tsx +0 -77
  86. package/src/extensions/nocobase-ai/components/chat/chat-message.tsx +0 -252
  87. package/src/extensions/nocobase-ai/components/chat/chat-messages.tsx +0 -173
  88. package/src/extensions/nocobase-ai/components/chat/chat-window.tsx +0 -118
  89. package/src/extensions/nocobase-ai/components/chat/conversation-list.tsx +0 -343
  90. package/src/extensions/nocobase-ai/components/chat/markdown-message.tsx +0 -165
  91. package/src/extensions/nocobase-ai/components/chat/model-select-options.tsx +0 -30
  92. package/src/extensions/nocobase-ai/components/chat/reasoning-panel.tsx +0 -29
  93. package/src/extensions/nocobase-ai/components/chat/sub-agent-conversation.tsx +0 -142
  94. package/src/extensions/nocobase-ai/components/chat/tool-call-card.tsx +0 -316
  95. package/src/extensions/nocobase-ai/components/chat/user-prompt-editor.tsx +0 -99
  96. package/src/extensions/nocobase-ai/components/chat/work-context-chip.tsx +0 -114
  97. package/src/extensions/nocobase-ai/components/index.ts +0 -59
  98. package/src/extensions/nocobase-ai/components/page-elements/ai-form.tsx +0 -44
  99. package/src/extensions/nocobase-ai/components/page-elements/page-element-provider.tsx +0 -469
  100. package/src/extensions/nocobase-ai/components/surfaces/chat-dialog.tsx +0 -31
  101. package/src/extensions/nocobase-ai/components/surfaces/chat-inline.tsx +0 -18
  102. package/src/extensions/nocobase-ai/components/surfaces/chat-page.tsx +0 -18
  103. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel-layout.tsx +0 -54
  104. package/src/extensions/nocobase-ai/components/surfaces/chat-side-panel.tsx +0 -74
  105. package/src/extensions/nocobase-ai/components/surfaces/chat-surface-actions.tsx +0 -54
  106. package/src/extensions/nocobase-ai/components/tools/builtin-tool-renderers.tsx +0 -31
  107. package/src/extensions/nocobase-ai/components/tools/business-report-dialog.tsx +0 -364
  108. package/src/extensions/nocobase-ai/components/tools/business-report-renderer.tsx +0 -122
  109. package/src/extensions/nocobase-ai/components/tools/business-report-utils.tsx +0 -282
  110. package/src/extensions/nocobase-ai/components/tools/chart-renderer.tsx +0 -99
  111. package/src/extensions/nocobase-ai/components/tools/echarts-preview.tsx +0 -59
  112. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-advanced.ts +0 -18
  113. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-common.ts +0 -20
  114. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-components.ts +0 -34
  115. package/src/extensions/nocobase-ai/components/tools/echarts-runtime-hierarchy.ts +0 -24
  116. package/src/extensions/nocobase-ai/components/tools/echarts-runtime.ts +0 -138
  117. package/src/extensions/nocobase-ai/components/tools/sub-agent-renderer.tsx +0 -67
  118. package/src/extensions/nocobase-ai/components/tools/suggestions-renderer.tsx +0 -59
  119. package/src/extensions/nocobase-ai/components/tools/tool-renderer-provider.tsx +0 -55
  120. package/src/extensions/nocobase-ai/components/tools/tool-renderer-utils.ts +0 -18
  121. package/src/extensions/nocobase-ai/components/tools/workflow-renderer.tsx +0 -108
  122. package/src/extensions/nocobase-ai/components/triggers/ai-chat-floating-trigger.tsx +0 -73
  123. package/src/extensions/nocobase-ai/components/triggers/ai-employee-shortcut.tsx +0 -149
  124. package/src/extensions/nocobase-ai/demo/configuration-gate.tsx +0 -49
  125. package/src/extensions/nocobase-ai/demo/container-showcase.tsx +0 -210
  126. package/src/extensions/nocobase-ai/demo/floating.tsx +0 -134
  127. package/src/extensions/nocobase-ai/demo/index.tsx +0 -346
  128. package/src/extensions/nocobase-ai/demo/interaction-showcase.tsx +0 -151
  129. package/src/extensions/nocobase-ai/demo/page-context-prompt-generator.tsx +0 -355
  130. package/src/extensions/nocobase-ai/demo/page-context.tsx +0 -884
  131. package/src/extensions/nocobase-ai/demo/page-element-showcase.tsx +0 -197
  132. package/src/extensions/nocobase-ai/demo/prompt-card.tsx +0 -41
  133. package/src/extensions/nocobase-ai/demo/prompt-generator.tsx +0 -452
  134. package/src/extensions/nocobase-ai/demo/shortcut.tsx +0 -1207
  135. package/src/extensions/nocobase-ai/demo/tool-cards.tsx +0 -530
  136. package/src/extensions/nocobase-ai/extension.tsx +0 -158
  137. package/src/extensions/nocobase-ai/global-ai-chat.tsx +0 -213
  138. package/src/extensions/nocobase-ai/index.ts +0 -3
  139. package/src/extensions/nocobase-ai/providers/ai-provider.tsx +0 -323
  140. package/src/extensions/nocobase-ai/providers/avatars.ts +0 -817
  141. package/src/extensions/nocobase-ai/providers/chat-context.tsx +0 -109
  142. package/src/extensions/nocobase-ai/providers/chat-controller.ts +0 -84
  143. package/src/extensions/nocobase-ai/providers/chat-message-utils.ts +0 -198
  144. package/src/extensions/nocobase-ai/providers/chat-provider.tsx +0 -871
  145. package/src/extensions/nocobase-ai/providers/chat-reducer.ts +0 -147
  146. package/src/extensions/nocobase-ai/providers/chat-task-utils.ts +0 -105
  147. package/src/extensions/nocobase-ai/providers/chat-transport.ts +0 -184
  148. package/src/extensions/nocobase-ai/providers/form-registry.tsx +0 -271
  149. package/src/extensions/nocobase-ai/providers/frontend-tool-registry.tsx +0 -231
  150. package/src/extensions/nocobase-ai/providers/index.ts +0 -61
  151. package/src/extensions/nocobase-ai/providers/model.ts +0 -32
  152. package/src/extensions/nocobase-ai/providers/page-context.tsx +0 -136
  153. package/src/extensions/nocobase-ai/providers/stream-coalescer.ts +0 -60
  154. package/src/extensions/nocobase-ai/providers/stream-event-utils.ts +0 -82
  155. package/src/extensions/nocobase-ai/providers/stream-parser.ts +0 -61
  156. package/src/extensions/nocobase-ai/providers/sub-agent-stream.ts +0 -316
  157. package/src/extensions/nocobase-ai/providers/types.ts +0 -183
  158. package/src/extensions/nocobase-ai/providers/ui-message-stream.ts +0 -463
  159. package/src/extensions/nocobase-ai/providers/use-automatic-tool-approval.ts +0 -94
  160. package/src/extensions/nocobase-ai/providers/use-chat-attachments.ts +0 -206
  161. package/src/extensions/nocobase-ai/providers/use-chat-message-actions.ts +0 -554
  162. package/src/extensions/nocobase-ai/providers/use-chat-runtime.ts +0 -210
  163. package/src/extensions/nocobase-ai/providers/use-chat-work-context.ts +0 -93
  164. package/src/extensions/nocobase-ai/providers/use-conversation-catalog.ts +0 -108
  165. package/src/extensions/nocobase-ai/providers/use-conversation-history.ts +0 -178
  166. package/src/extensions/nocobase-ai/services/index.ts +0 -7
  167. package/src/extensions/nocobase-ai/services/nocobase-ai-service.ts +0 -454
  168. package/src/extensions/nocobase-ai/services/types.ts +0 -82
  169. /package/{registry/nocobase-i18n → src/providers/i18n}/i18n-provider.ts +0 -0
  170. /package/{registry/nocobase-i18n → src/providers/i18n}/locale-store.ts +0 -0
@@ -1,231 +0,0 @@
1
- import {
2
- createContext,
3
- useContext,
4
- useMemo,
5
- type PropsWithChildren,
6
- } from "react";
7
- import type { AIToolCallInvocationContext, AIToolInvokerMap } from "./types";
8
-
9
- export type AIFrontendToolPermission = "ASK" | "ALLOW";
10
-
11
- export type AIFrontendToolRegistration<
12
- TArgs = unknown,
13
- TResult = unknown,
14
- > = {
15
- name: string;
16
- title?: string;
17
- description: string;
18
- permission?: AIFrontendToolPermission;
19
- inputSchema?: Record<string, unknown>;
20
- execute: (args: TArgs) => TResult | Promise<TResult>;
21
- };
22
-
23
- export type AIFrontendToolManifest = {
24
- id: string;
25
- blockUid: string;
26
- name: string;
27
- title?: string;
28
- description: string;
29
- permission: AIFrontendToolPermission;
30
- inputSchema: Record<string, unknown>;
31
- };
32
-
33
- type AIFrontendToolEntry = {
34
- token: symbol;
35
- manifest: AIFrontendToolManifest;
36
- execute: (args: unknown) => unknown | Promise<unknown>;
37
- };
38
-
39
- const TOOL_NAME_PATTERN = /^[A-Za-z][A-Za-z0-9_-]{0,63}$/;
40
-
41
- const cloneManifest = (
42
- manifest: AIFrontendToolManifest
43
- ): AIFrontendToolManifest => structuredClone(manifest);
44
-
45
- const isRecord = (value: unknown): value is Record<string, unknown> =>
46
- !!value && typeof value === "object" && !Array.isArray(value);
47
-
48
- export function defineAIFrontendTool<TArgs, TResult>(
49
- registration: AIFrontendToolRegistration<TArgs, TResult>
50
- ) {
51
- return registration;
52
- }
53
-
54
- export class AIFrontendToolRegistry {
55
- private readonly tools = new Map<string, AIFrontendToolEntry>();
56
-
57
- register(contextId: string, registration: AIFrontendToolRegistration) {
58
- if (!contextId.trim()) {
59
- throw new Error("Frontend Tool context id is required");
60
- }
61
- if (!TOOL_NAME_PATTERN.test(registration.name)) {
62
- throw new Error(
63
- "Frontend Tool name must start with a letter and contain only letters, numbers, underscores, or hyphens"
64
- );
65
- }
66
- if (!registration.description.trim()) {
67
- throw new Error("Frontend Tool description is required");
68
- }
69
- if (typeof registration.execute !== "function") {
70
- throw new Error("Frontend Tool execute must be a function");
71
- }
72
- if (
73
- registration.inputSchema !== undefined &&
74
- !isRecord(registration.inputSchema)
75
- ) {
76
- throw new Error("Frontend Tool inputSchema must be an object");
77
- }
78
- if (
79
- registration.permission !== undefined &&
80
- registration.permission !== "ASK" &&
81
- registration.permission !== "ALLOW"
82
- ) {
83
- throw new Error("Frontend Tool permission must be ASK or ALLOW");
84
- }
85
-
86
- const id = `${contextId}:${registration.name}`;
87
- if (this.tools.has(id)) {
88
- throw new Error(
89
- `Frontend Tool "${registration.name}" is already registered for context "${contextId}"`
90
- );
91
- }
92
- const token = Symbol(id);
93
- let inputSchema: Record<string, unknown>;
94
- try {
95
- inputSchema = structuredClone(
96
- registration.inputSchema ?? { type: "object", properties: {} }
97
- );
98
- } catch {
99
- throw new Error("Frontend Tool inputSchema must be serializable");
100
- }
101
- const manifest: AIFrontendToolManifest = {
102
- id,
103
- blockUid: contextId,
104
- name: registration.name,
105
- title: registration.title,
106
- description: registration.description,
107
- permission: registration.permission ?? "ASK",
108
- inputSchema,
109
- };
110
- this.tools.set(id, {
111
- token,
112
- manifest,
113
- execute: registration.execute as (
114
- args: unknown
115
- ) => unknown | Promise<unknown>,
116
- });
117
- return () => {
118
- if (this.tools.get(id)?.token === token) this.tools.delete(id);
119
- };
120
- }
121
-
122
- list(contextId: string) {
123
- return [...this.tools.values()]
124
- .filter((entry) => entry.manifest.blockUid === contextId)
125
- .map((entry) => cloneManifest(entry.manifest));
126
- }
127
-
128
- getManifest(toolId: string) {
129
- const manifest = this.tools.get(toolId)?.manifest;
130
- return manifest ? cloneManifest(manifest) : undefined;
131
- }
132
-
133
- async execute(toolId: string, args: unknown) {
134
- const entry = this.tools.get(toolId);
135
- if (!entry) throw new Error(`Frontend Tool "${toolId}" is unavailable`);
136
- const result = await entry.execute(args);
137
- try {
138
- structuredClone(result);
139
- JSON.stringify(result);
140
- } catch {
141
- throw new Error(
142
- `Frontend Tool "${toolId}" returned a non-serializable result`
143
- );
144
- }
145
- return result;
146
- }
147
- }
148
-
149
- const AIFrontendToolRegistryContext =
150
- createContext<AIFrontendToolRegistry | null>(null);
151
-
152
- export function AIFrontendToolRegistryProvider({
153
- children,
154
- }: PropsWithChildren) {
155
- const registry = useMemo(() => new AIFrontendToolRegistry(), []);
156
- return (
157
- <AIFrontendToolRegistryContext.Provider value={registry}>
158
- {children}
159
- </AIFrontendToolRegistryContext.Provider>
160
- );
161
- }
162
-
163
- export function useAIFrontendToolRegistry() {
164
- const registry = useContext(AIFrontendToolRegistryContext);
165
- if (!registry) {
166
- throw new Error(
167
- "useAIFrontendToolRegistry must be used inside AIFrontendToolRegistryProvider"
168
- );
169
- }
170
- return registry;
171
- }
172
-
173
- export function useOptionalAIFrontendToolRegistry() {
174
- return useContext(AIFrontendToolRegistryContext);
175
- }
176
-
177
- const errorResult = (error: unknown) => ({
178
- status: "error" as const,
179
- content: error instanceof Error ? error.message : String(error),
180
- });
181
-
182
- const getToolId = (input: unknown) =>
183
- input && typeof input === "object" && !Array.isArray(input)
184
- ? (input as { toolId?: unknown }).toolId
185
- : undefined;
186
-
187
- const isToolAllowed = (
188
- toolId: string,
189
- context: AIToolCallInvocationContext
190
- ) => context.allowedFrontendToolIds?.includes(toolId) === true;
191
-
192
- export function createFrontendToolInvokers(
193
- registry: AIFrontendToolRegistry
194
- ): AIToolInvokerMap {
195
- return {
196
- loadFrontendTool: async (input, context) => {
197
- const toolId = getToolId(input);
198
- if (typeof toolId !== "string" || !toolId) {
199
- return errorResult("Frontend Tool id is required");
200
- }
201
- if (!isToolAllowed(toolId, context)) {
202
- return errorResult(
203
- `Frontend Tool "${toolId}" is not available in this conversation context`
204
- );
205
- }
206
- return (
207
- registry.getManifest(toolId) ??
208
- errorResult(`Frontend Tool "${toolId}" is unavailable`)
209
- );
210
- },
211
- executeFrontendTool: async (input, context) => {
212
- const params =
213
- input && typeof input === "object" && !Array.isArray(input)
214
- ? (input as { toolId?: unknown; args?: unknown })
215
- : {};
216
- if (typeof params.toolId !== "string" || !params.toolId) {
217
- return errorResult("Frontend Tool id is required");
218
- }
219
- if (!isToolAllowed(params.toolId, context)) {
220
- return errorResult(
221
- `Frontend Tool "${params.toolId}" is not available in this conversation context`
222
- );
223
- }
224
- try {
225
- return await registry.execute(params.toolId, params.args ?? {});
226
- } catch (error) {
227
- return errorResult(error);
228
- }
229
- },
230
- };
231
- }
@@ -1,61 +0,0 @@
1
- export {
2
- AIProvider,
3
- useAI,
4
- useGlobalAIChatController,
5
- type AIProviderProps,
6
- } from "./ai-provider";
7
- export { AIChatProvider, type AIChatProviderProps } from "./chat-provider";
8
- export {
9
- useAIChat,
10
- useAIChatBase,
11
- useAIChatMessages,
12
- useAIChatStatus,
13
- type AIChatBaseContextValue,
14
- type AIChatContextValue,
15
- type AIChatMessagesContextValue,
16
- type AIChatStatusContextValue,
17
- } from "./chat-context";
18
- export {
19
- createAIChatController,
20
- useAIChatController,
21
- useAIChatControllerState,
22
- type AIChatController,
23
- type AIChatControllerSnapshot,
24
- } from "./chat-controller";
25
- export { NocoBaseChatTransport } from "./chat-transport";
26
- export { getAIEmployeeAvatar } from "./avatars";
27
- export { findAIModel, getAIModelKey, groupAIModels } from "./model";
28
- export type { AIModelGroup } from "./model";
29
- export {
30
- AIFormRegistry,
31
- AIFormRegistryProvider,
32
- createFormFillerInvoker,
33
- useAIFormRegistry,
34
- type AIFormField,
35
- type AIFormFillResult,
36
- type AIFormFillSkippedField,
37
- type AIFormTarget,
38
- } from "./form-registry";
39
- export {
40
- AIFrontendToolRegistry,
41
- AIFrontendToolRegistryProvider,
42
- createFrontendToolInvokers,
43
- defineAIFrontendTool,
44
- useAIFrontendToolRegistry,
45
- useOptionalAIFrontendToolRegistry,
46
- type AIFrontendToolManifest,
47
- type AIFrontendToolPermission,
48
- type AIFrontendToolRegistration,
49
- } from "./frontend-tool-registry";
50
- export {
51
- AIPageContextResolverProvider,
52
- AIPageContextScope,
53
- createAIPageContextReference,
54
- getAIWorkContextRequiredTools,
55
- getAIWorkContextToolScope,
56
- mergeAIRequiredTools,
57
- useAIPageContextScope,
58
- useAIPageContextResolver,
59
- type AIPageContextResolver,
60
- } from "./page-context";
61
- export type * from "./types";
@@ -1,32 +0,0 @@
1
- import type { AIModel } from "./types";
2
-
3
- export type AIModelGroup = {
4
- key: string;
5
- label: string;
6
- models: AIModel[];
7
- };
8
-
9
- export function getAIModelKey(model: AIModel) {
10
- return model.llmService ? `${model.llmService}:${model.value}` : model.value;
11
- }
12
-
13
- export function findAIModel(models: AIModel[], key: string) {
14
- return models.find((model) => getAIModelKey(model) === key);
15
- }
16
-
17
- export function groupAIModels(models: AIModel[]): AIModelGroup[] {
18
- const groups = new Map<string, AIModelGroup>();
19
-
20
- for (const model of models) {
21
- const key = model.llmService ?? "__models__";
22
- const group = groups.get(key) ?? {
23
- key,
24
- label: model.llmServiceTitle ?? model.llmService ?? "Models",
25
- models: [],
26
- };
27
- group.models.push(model);
28
- groups.set(key, group);
29
- }
30
-
31
- return Array.from(groups.values());
32
- }
@@ -1,136 +0,0 @@
1
- import {
2
- createContext,
3
- useContext,
4
- useMemo,
5
- type PropsWithChildren,
6
- } from "react";
7
- import type {
8
- AIEmployeeTask,
9
- AIWorkContextItem,
10
- } from "./types";
11
-
12
- export type AIPageContextResolver = (
13
- items: AIWorkContextItem[]
14
- ) => Promise<AIWorkContextItem[]>;
15
-
16
- const AIPageContextResolverContext = createContext<
17
- AIPageContextResolver | undefined
18
- >(undefined);
19
-
20
- export function AIPageContextResolverProvider({
21
- resolve,
22
- children,
23
- }: PropsWithChildren<{ resolve: AIPageContextResolver }>) {
24
- return (
25
- <AIPageContextResolverContext.Provider value={resolve}>
26
- {children}
27
- </AIPageContextResolverContext.Provider>
28
- );
29
- }
30
-
31
- export const useAIPageContextResolver = () =>
32
- useContext(AIPageContextResolverContext);
33
-
34
- const EMPTY_PAGE_CONTEXT: AIWorkContextItem[] = [];
35
- const AIPageContextScopeContext = createContext(EMPTY_PAGE_CONTEXT);
36
-
37
- export function AIPageContextScope({
38
- context,
39
- mode = "replace",
40
- children,
41
- }: PropsWithChildren<{
42
- context: AIWorkContextItem | AIWorkContextItem[];
43
- mode?: "replace" | "append";
44
- }>) {
45
- const inheritedContext = useContext(AIPageContextScopeContext);
46
- const value = useMemo(
47
- () => {
48
- const ownContext = Array.isArray(context) ? context : [context];
49
- if (mode !== "append") return ownContext;
50
- const combined = [...inheritedContext, ...ownContext];
51
- return combined.filter((item, index) => {
52
- if (!item.id) return true;
53
- return (
54
- combined.findLastIndex(
55
- (candidate) =>
56
- candidate.type === item.type && candidate.id === item.id
57
- ) === index
58
- );
59
- });
60
- },
61
- [context, inheritedContext, mode]
62
- );
63
- return (
64
- <AIPageContextScopeContext.Provider value={value}>
65
- {children}
66
- </AIPageContextScopeContext.Provider>
67
- );
68
- }
69
-
70
- export const useAIPageContextScope = () =>
71
- useContext(AIPageContextScopeContext);
72
-
73
- export function createAIPageContextReference({
74
- id,
75
- title,
76
- kind,
77
- }: {
78
- id: string;
79
- title: string;
80
- kind?: string;
81
- }): AIWorkContextItem {
82
- return {
83
- type: "page-element",
84
- id,
85
- title,
86
- ...(kind ? { kind } : {}),
87
- };
88
- }
89
-
90
- const isFormContext = (item: AIWorkContextItem) => {
91
- if (item.kind === "form") return true;
92
- if (!item.content || typeof item.content !== "object") return false;
93
- const content = item.content as Record<string, unknown>;
94
- return typeof content.form === "string" && Array.isArray(content.fields);
95
- };
96
-
97
- export function getAIWorkContextRequiredTools(items: AIWorkContextItem[]) {
98
- return items.some(isFormContext) ? ["formFiller"] : [];
99
- }
100
-
101
- export function getAIWorkContextToolScope(items: AIWorkContextItem[]) {
102
- const frontendToolIds = new Set<string>();
103
- const formIds = new Set<string>();
104
- for (const item of items) {
105
- if (Array.isArray(item.frontendTools)) {
106
- for (const tool of item.frontendTools) {
107
- if (
108
- tool &&
109
- typeof tool === "object" &&
110
- typeof (tool as { id?: unknown }).id === "string"
111
- ) {
112
- frontendToolIds.add((tool as { id: string }).id);
113
- }
114
- }
115
- }
116
- if (isFormContext(item)) {
117
- const form = (item.content as { form?: unknown } | undefined)?.form;
118
- if (typeof form === "string" && form) formIds.add(form);
119
- }
120
- }
121
- return {
122
- allowedFrontendToolIds: [...frontendToolIds],
123
- allowedFormIds: [...formIds],
124
- };
125
- }
126
-
127
- export function mergeAIRequiredTools(
128
- skillSettings: AIEmployeeTask["skillSettings"],
129
- requiredTools: string[]
130
- ): AIEmployeeTask["skillSettings"] {
131
- if (!requiredTools.length) return skillSettings;
132
- return {
133
- ...skillSettings,
134
- tools: [...new Set([...(skillSettings?.tools ?? []), ...requiredTools])],
135
- };
136
- }
@@ -1,60 +0,0 @@
1
- export type StreamCoalescerOptions<Key, Value> = {
2
- interval: number;
3
- maxSize: number;
4
- getSize: (value: Value) => number;
5
- merge: (current: Value, incoming: Value) => Value;
6
- onFlush: (key: Key, value: Value) => void;
7
- };
8
-
9
- type PendingValue<Value> = {
10
- value: Value;
11
- timer: ReturnType<typeof setTimeout>;
12
- };
13
-
14
- export class StreamCoalescer<Key, Value> {
15
- private readonly pending = new Map<Key, PendingValue<Value>>();
16
-
17
- constructor(private readonly options: StreamCoalescerOptions<Key, Value>) {}
18
-
19
- push(key: Key, value: Value) {
20
- const existing = this.pending.get(key);
21
- if (existing) {
22
- existing.value = this.options.merge(existing.value, value);
23
- if (this.options.getSize(existing.value) >= this.options.maxSize) {
24
- this.flush(key);
25
- }
26
- return;
27
- }
28
-
29
- const timer = setTimeout(() => this.flush(key), this.options.interval);
30
- this.pending.set(key, { value, timer });
31
- if (this.options.getSize(value) >= this.options.maxSize) {
32
- this.flush(key);
33
- }
34
- }
35
-
36
- has(key: Key) {
37
- return this.pending.has(key);
38
- }
39
-
40
- flush(key: Key) {
41
- const pending = this.pending.get(key);
42
- if (!pending) return;
43
- clearTimeout(pending.timer);
44
- this.pending.delete(key);
45
- this.options.onFlush(key, pending.value);
46
- }
47
-
48
- flushAll() {
49
- for (const key of [...this.pending.keys()]) {
50
- this.flush(key);
51
- }
52
- }
53
-
54
- clear() {
55
- for (const pending of this.pending.values()) {
56
- clearTimeout(pending.timer);
57
- }
58
- this.pending.clear();
59
- }
60
- }
@@ -1,82 +0,0 @@
1
- import type { NocoBaseStreamEvent } from "./stream-parser";
2
-
3
- export type NocoBaseToolCall = {
4
- id?: string;
5
- index?: number;
6
- name?: string;
7
- args?: unknown;
8
- input?: unknown;
9
- output?: unknown;
10
- content?: unknown;
11
- status?: string;
12
- invokeStatus?: string;
13
- invokeStartTime?: unknown;
14
- invokeEndTime?: unknown;
15
- messageId?: string;
16
- willInterrupt?: boolean;
17
- auto?: boolean;
18
- selectedSuggestion?: string;
19
- };
20
-
21
- export const isRecord = (value: unknown): value is Record<string, unknown> =>
22
- !!value && typeof value === "object" && !Array.isArray(value);
23
-
24
- export const toolCallsFromEvent = (event: NocoBaseStreamEvent) => {
25
- if (Array.isArray(event.body)) return event.body as NocoBaseToolCall[];
26
- if (!isRecord(event.body) || !Array.isArray(event.body.toolCalls)) return [];
27
- return event.body.toolCalls as NocoBaseToolCall[];
28
- };
29
-
30
- export const parseToolInput = (input: unknown) => {
31
- if (typeof input !== "string") return input;
32
- try {
33
- return JSON.parse(input) as unknown;
34
- } catch {
35
- return input;
36
- }
37
- };
38
-
39
- const completedToolStatus = new Set(["success", "done", "confirmed"]);
40
- const failedToolStatus = new Set(["error", "failed", "rejected"]);
41
- const pendingInvokeStatus = new Set([
42
- "interrupted",
43
- "waiting",
44
- "pending",
45
- "running",
46
- ]);
47
-
48
- export const getToolCallState = (toolCall: NocoBaseToolCall) => {
49
- const invokeStatus = String(toolCall.invokeStatus ?? "").toLowerCase();
50
- const resultStatus = String(toolCall.status ?? "").toLowerCase();
51
- const failed =
52
- failedToolStatus.has(invokeStatus) || failedToolStatus.has(resultStatus);
53
- const completed =
54
- !failed &&
55
- (completedToolStatus.has(invokeStatus) ||
56
- (!invokeStatus && completedToolStatus.has(resultStatus)));
57
- return {
58
- invokeStatus,
59
- resultStatus,
60
- failed,
61
- completed,
62
- pending: !failed && !completed && pendingInvokeStatus.has(invokeStatus),
63
- };
64
- };
65
-
66
- export const getToolProviderMetadata = (toolCall: NocoBaseToolCall) => {
67
- const { invokeStatus, resultStatus } = getToolCallState(toolCall);
68
- return {
69
- nocobase: {
70
- requiresApproval:
71
- (invokeStatus === "interrupted" ||
72
- (!invokeStatus && toolCall.willInterrupt === true)) &&
73
- toolCall.auto !== true,
74
- autoApprove: invokeStatus === "interrupted" && toolCall.auto === true,
75
- invokeStatus,
76
- ...(typeof toolCall.selectedSuggestion === "string"
77
- ? { selectedSuggestion: toolCall.selectedSuggestion }
78
- : {}),
79
- status: resultStatus,
80
- },
81
- };
82
- };
@@ -1,61 +0,0 @@
1
- import {
2
- createParser,
3
- type EventSourceMessage,
4
- type ParseError,
5
- } from "eventsource-parser";
6
-
7
- export type NocoBaseStreamEvent = {
8
- type: string;
9
- body?: unknown;
10
- sessionId?: string;
11
- from?: string;
12
- username?: string;
13
- errorName?: string;
14
- };
15
-
16
- const MAX_SSE_BUFFER_SIZE = 16 * 1024 * 1024;
17
-
18
- export async function* parseNocoBaseSSE(
19
- stream: ReadableStream<Uint8Array>
20
- ): AsyncGenerator<NocoBaseStreamEvent> {
21
- const reader = stream.getReader();
22
- const decoder = new TextDecoder();
23
- const events: EventSourceMessage[] = [];
24
- let parseError: ParseError | undefined;
25
- const parser = createParser({
26
- maxBufferSize: MAX_SSE_BUFFER_SIZE,
27
- onEvent: (event) => events.push(event),
28
- onError: (error) => {
29
- parseError = error;
30
- },
31
- });
32
-
33
- const drainEvents = function* () {
34
- while (events.length) {
35
- const event = events.shift();
36
- if (!event) continue;
37
- const payload = event.data.trim();
38
- if (!payload || payload === "[DONE]") continue;
39
- yield JSON.parse(payload) as NocoBaseStreamEvent;
40
- }
41
- };
42
-
43
- try {
44
- while (true) {
45
- const { done, value } = await reader.read();
46
- if (value) parser.feed(decoder.decode(value, { stream: true }));
47
- if (parseError) throw parseError;
48
- yield* drainEvents();
49
- if (!done) continue;
50
-
51
- const remainder = decoder.decode();
52
- if (remainder) parser.feed(remainder);
53
- parser.reset({ consume: true });
54
- if (parseError) throw parseError;
55
- yield* drainEvents();
56
- return;
57
- }
58
- } finally {
59
- reader.releaseLock();
60
- }
61
- }