orchid-ai 1.2.7 → 1.3.0
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/dist/cli/hooks/useResolvedDefaultModel.d.ts +3 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/server/contextual-service.d.ts +1 -0
- package/dist/cli/types/types.d.ts +20 -0
- package/dist/hooks/useResolvedDefaultModel.d.ts +3 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.esm.js +27 -6
- package/dist/index.js +27 -6
- package/dist/server/contextual-service.d.ts +1 -0
- package/dist/server/hooks/useResolvedDefaultModel.d.ts +3 -1
- package/dist/server/index.esm.js +17 -0
- package/dist/server/index.js +17 -0
- package/dist/server/server/contextual-service.d.ts +1 -0
- package/dist/server/types/types.d.ts +20 -0
- package/dist/types/types.d.ts +20 -0
- package/package.json +1 -1
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ModelCapabilities } from '../types/types';
|
|
2
|
+
import type { ServerConfig } from '../types/types';
|
|
2
3
|
/**
|
|
3
4
|
* Hook to resolve tier-based default model config to actual model
|
|
4
5
|
*
|
|
@@ -8,13 +9,14 @@ import type { ModelCapabilities } from '../types/types';
|
|
|
8
9
|
* match for the provider and tier combination.
|
|
9
10
|
*
|
|
10
11
|
* @param defaultModel - The model configuration to resolve
|
|
12
|
+
* @param serverConfig - Optional server config to build the models endpoint URL
|
|
11
13
|
* @returns The resolved model with provider, model name, and capabilities
|
|
12
14
|
*/
|
|
13
15
|
export declare function useResolvedDefaultModel(defaultModel?: {
|
|
14
16
|
provider: string;
|
|
15
17
|
model?: string;
|
|
16
18
|
tier?: 'fast' | 'balanced' | 'powerful';
|
|
17
|
-
}): {
|
|
19
|
+
}, serverConfig?: ServerConfig): {
|
|
18
20
|
provider: string;
|
|
19
21
|
model: string;
|
|
20
22
|
capabilities: ModelCapabilities;
|
package/dist/cli/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export { mergeWithAi } from './utils';
|
|
|
10
10
|
export { useAiMerge } from './hooks/useAiMerge';
|
|
11
11
|
export { ErrorBoundary } from './components/ErrorBoundary';
|
|
12
12
|
export { Icon as CommandIcon } from './components/Icon';
|
|
13
|
-
export type { CommandConfig, TrainingData, AIProvider, CommandTheme, CommandSuggestion, CommandPopupProps, ChatMessage, ChatTheme, AIStatus, Action, ModelInfo, ServerConfig, SchemaDefinition, SchemaProperty, ContextualServiceConfig, ContextualRequest, } from './types/types';
|
|
13
|
+
export type { CommandConfig, TrainingData, AIProvider, CommandTheme, CommandSuggestion, CommandPopupProps, ChatMessage, ChatTheme, AIStatus, Action, ModelInfo, ServerConfig, ClientConfig, SchemaDefinition, SchemaProperty, ContextualServiceConfig, ContextualRequest, } from './types/types';
|
|
14
14
|
export { defaultTheme as defaultCommandTheme } from './constants';
|
|
@@ -134,6 +134,24 @@ export interface ServerConfig {
|
|
|
134
134
|
additionalContext?: string;
|
|
135
135
|
stayOnPage?: boolean;
|
|
136
136
|
}
|
|
137
|
+
export interface ClientConfig {
|
|
138
|
+
models?: Record<string, ModelInfo[]>;
|
|
139
|
+
defaultModel?: {
|
|
140
|
+
provider: string;
|
|
141
|
+
model?: string;
|
|
142
|
+
tier?: 'fast' | 'balanced' | 'powerful';
|
|
143
|
+
};
|
|
144
|
+
showUsageStats?: boolean;
|
|
145
|
+
maxFileSize?: string;
|
|
146
|
+
features?: {
|
|
147
|
+
modelSwitching?: boolean;
|
|
148
|
+
usageTracking?: boolean;
|
|
149
|
+
imageAnalysis?: boolean;
|
|
150
|
+
fileUploads?: boolean;
|
|
151
|
+
enableImageUploads?: boolean;
|
|
152
|
+
};
|
|
153
|
+
schemas?: Record<string, SchemaDefinition>;
|
|
154
|
+
}
|
|
137
155
|
export interface TrainingDataPaths {
|
|
138
156
|
components: string[];
|
|
139
157
|
schemas: string[];
|
|
@@ -465,6 +483,8 @@ export interface SchemaProperty {
|
|
|
465
483
|
maxLength?: number;
|
|
466
484
|
minimum?: number;
|
|
467
485
|
maximum?: number;
|
|
486
|
+
/** If true, this field is not to be generated by AI (e.g. auto-generated fields) */
|
|
487
|
+
skip?: boolean;
|
|
468
488
|
}
|
|
469
489
|
/**
|
|
470
490
|
* Simplified config for ContextualCommandService
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ModelCapabilities } from '../types/types';
|
|
2
|
+
import type { ServerConfig } from '../types/types';
|
|
2
3
|
/**
|
|
3
4
|
* Hook to resolve tier-based default model config to actual model
|
|
4
5
|
*
|
|
@@ -8,13 +9,14 @@ import type { ModelCapabilities } from '../types/types';
|
|
|
8
9
|
* match for the provider and tier combination.
|
|
9
10
|
*
|
|
10
11
|
* @param defaultModel - The model configuration to resolve
|
|
12
|
+
* @param serverConfig - Optional server config to build the models endpoint URL
|
|
11
13
|
* @returns The resolved model with provider, model name, and capabilities
|
|
12
14
|
*/
|
|
13
15
|
export declare function useResolvedDefaultModel(defaultModel?: {
|
|
14
16
|
provider: string;
|
|
15
17
|
model?: string;
|
|
16
18
|
tier?: 'fast' | 'balanced' | 'powerful';
|
|
17
|
-
}): {
|
|
19
|
+
}, serverConfig?: ServerConfig): {
|
|
18
20
|
provider: string;
|
|
19
21
|
model: string;
|
|
20
22
|
capabilities: ModelCapabilities;
|
package/dist/index.d.ts
CHANGED
|
@@ -10,5 +10,5 @@ export { mergeWithAi } from './utils';
|
|
|
10
10
|
export { useAiMerge } from './hooks/useAiMerge';
|
|
11
11
|
export { ErrorBoundary } from './components/ErrorBoundary';
|
|
12
12
|
export { Icon as CommandIcon } from './components/Icon';
|
|
13
|
-
export type { CommandConfig, TrainingData, AIProvider, CommandTheme, CommandSuggestion, CommandPopupProps, ChatMessage, ChatTheme, AIStatus, Action, ModelInfo, ServerConfig, SchemaDefinition, SchemaProperty, ContextualServiceConfig, ContextualRequest, } from './types/types';
|
|
13
|
+
export type { CommandConfig, TrainingData, AIProvider, CommandTheme, CommandSuggestion, CommandPopupProps, ChatMessage, ChatTheme, AIStatus, Action, ModelInfo, ServerConfig, ClientConfig, SchemaDefinition, SchemaProperty, ContextualServiceConfig, ContextualRequest, } from './types/types';
|
|
14
14
|
export { defaultTheme as defaultCommandTheme } from './constants';
|
package/dist/index.esm.js
CHANGED
|
@@ -1100,6 +1100,22 @@ function useDebouncedSuggestions(query, options) {
|
|
|
1100
1100
|
};
|
|
1101
1101
|
}
|
|
1102
1102
|
|
|
1103
|
+
// Helper function to build URL from config (reused from useModelSwitcher)
|
|
1104
|
+
function buildUrlFromConfig$1(config) {
|
|
1105
|
+
if (!config)
|
|
1106
|
+
return '';
|
|
1107
|
+
const suffix = config.suffix || '';
|
|
1108
|
+
if (suffix.startsWith('http://') || suffix.startsWith('https://')) {
|
|
1109
|
+
return suffix;
|
|
1110
|
+
}
|
|
1111
|
+
if (typeof window === 'undefined') {
|
|
1112
|
+
return suffix;
|
|
1113
|
+
}
|
|
1114
|
+
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
|
|
1115
|
+
const hostname = window.location.hostname;
|
|
1116
|
+
const port = window.location.port ? `:${window.location.port}` : '';
|
|
1117
|
+
return `${protocol}//${hostname}${port}${suffix}`;
|
|
1118
|
+
}
|
|
1103
1119
|
/**
|
|
1104
1120
|
* Hook to resolve tier-based default model config to actual model
|
|
1105
1121
|
*
|
|
@@ -1109,9 +1125,10 @@ function useDebouncedSuggestions(query, options) {
|
|
|
1109
1125
|
* match for the provider and tier combination.
|
|
1110
1126
|
*
|
|
1111
1127
|
* @param defaultModel - The model configuration to resolve
|
|
1128
|
+
* @param serverConfig - Optional server config to build the models endpoint URL
|
|
1112
1129
|
* @returns The resolved model with provider, model name, and capabilities
|
|
1113
1130
|
*/
|
|
1114
|
-
function useResolvedDefaultModel(defaultModel) {
|
|
1131
|
+
function useResolvedDefaultModel(defaultModel, serverConfig) {
|
|
1115
1132
|
const [resolvedModel, setResolvedModel] = useState();
|
|
1116
1133
|
useEffect(() => {
|
|
1117
1134
|
if (!defaultModel || defaultModel.model) {
|
|
@@ -1128,7 +1145,11 @@ function useResolvedDefaultModel(defaultModel) {
|
|
|
1128
1145
|
// Need to resolve tier to actual model
|
|
1129
1146
|
const resolveModel = async () => {
|
|
1130
1147
|
try {
|
|
1131
|
-
|
|
1148
|
+
// Build models endpoint URL from serverConfig if provided, otherwise use default
|
|
1149
|
+
const endpoint = serverConfig
|
|
1150
|
+
? `${buildUrlFromConfig$1(serverConfig)}/models`
|
|
1151
|
+
: '/command/models';
|
|
1152
|
+
const response = await fetch(endpoint);
|
|
1132
1153
|
if (!response.ok)
|
|
1133
1154
|
throw new Error('Failed to fetch models');
|
|
1134
1155
|
const data = await response.json();
|
|
@@ -1155,7 +1176,7 @@ function useResolvedDefaultModel(defaultModel) {
|
|
|
1155
1176
|
}
|
|
1156
1177
|
};
|
|
1157
1178
|
resolveModel();
|
|
1158
|
-
}, [defaultModel]);
|
|
1179
|
+
}, [defaultModel, serverConfig]);
|
|
1159
1180
|
return resolvedModel;
|
|
1160
1181
|
}
|
|
1161
1182
|
|
|
@@ -3302,7 +3323,7 @@ theme, isOpen, }) {
|
|
|
3302
3323
|
}, [onModelSelectionChange]);
|
|
3303
3324
|
return (jsxs("div", { className: `px-3 py-2 border-t flex flex-col gap-2 transition-all duration-500 delay-500 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`, style: { borderColor: theme.colors.border.primary }, children: [attachedFiles.length > 0 && (jsx("div", { className: "flex flex-wrap gap-2 mb-2", children: attachedFiles.map((file, index) => (jsxs("div", { className: "relative group", children: [jsx("div", { className: "w-16 h-16 rounded-lg border border-gray-200 bg-gray-50 flex items-center justify-center overflow-hidden", children: file.type.startsWith('image/') ? (jsx("img", { src: URL.createObjectURL(file), alt: `Attached ${index + 1}`, className: "w-full h-full object-cover" })) : (jsx("div", { className: "flex items-center justify-center", children: FileHandler.getFileIcon(file) })) }), jsx("button", { onClick: () => removeFile(index), className: "absolute -top-2 -right-2 w-5 h-5 bg-red-500 text-white rounded-full flex items-center justify-center text-xs hover:bg-red-600 transition-colors", title: "Remove file", children: "\u00D7" }), jsx("div", { className: "absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10 mt-2", children: jsxs("div", { className: "bg-gray-900 text-white text-xs rounded-lg px-2 py-1 whitespace-nowrap shadow-lg", children: [jsx("div", { className: "font-medium truncate max-w-32", title: file.name, children: file.name }), jsxs("div", { className: "text-gray-300", children: [FileHandler.getFileTypeDescription(file), " \u2022", ' ', FileHandler.formatFileSize(file.size)] }), jsx("div", { className: "absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-gray-900 rotate-45" })] }) })] }, index))) })), incompatibleFiles.length > 0 && (jsxs("div", { className: "relative flex flex-col gap-1 text-sm text-orange-700 bg-orange-50 border border-orange-200 rounded px-3 py-2 mb-2", children: [jsx("button", { onClick: () => setIncompatibleFiles([]), className: "absolute top-1 right-1 w-5 h-5 flex items-center justify-center text-orange-500 hover:text-orange-700 hover:bg-orange-200 rounded-full transition-colors", title: "Dismiss", type: "button", children: "\u00D7" }), jsxs("div", { className: "flex items-center gap-2 pr-6", children: [jsx("span", { children: "\u26A0\uFE0F" }), jsx("span", { className: "font-medium", children: incompatibleFiles.length === 1
|
|
3304
3325
|
? 'File not supported'
|
|
3305
|
-
: 'Some files not supported' })] }), jsxs("div", { className: "text-xs", children: [jsx("div", { className: "mb-1", children: "These files were skipped:" }), jsxs("ul", { className: "list-disc list-inside ml-2 space-y-0.5", children: [incompatibleFiles.slice(0, 3).map((fileName, index) => (jsx("li", { className: "truncate", children: fileName }, index))), incompatibleFiles.length > 3 && (jsxs("li", { children: ["... and ", incompatibleFiles.length - 3, " more"] }))] }), jsxs("div", { className: "mt-1 text-orange-600", children: ["\uD83D\uDCA1 Only ", FileHandler.getAcceptedTypesDescription(), " are supported", incompatibleFiles.some((name) => name.includes('images not supported by current model')) && (
|
|
3326
|
+
: 'Some files not supported' })] }), jsxs("div", { className: "text-xs", children: [jsx("div", { className: "mb-1", children: "These files were skipped:" }), jsxs("ul", { className: "list-disc list-inside ml-2 space-y-0.5", children: [incompatibleFiles.slice(0, 3).map((fileName, index) => (jsx("li", { className: "truncate", children: fileName }, index))), incompatibleFiles.length > 3 && (jsxs("li", { children: ["... and ", incompatibleFiles.length - 3, " more"] }))] }), jsxs("div", { className: "mt-1 text-orange-600", children: ["\uD83D\uDCA1 Only ", FileHandler.getAcceptedTypesDescription(), " are supported", incompatibleFiles.some((name) => name.includes('images not supported by current model')) && (jsx("div", { className: "mt-1", children: "\uD83D\uDD04 Switch to a model showing the image icon in the model switcher to enable image uploads" }))] })] })] })), jsxs("div", { className: `relative flex flex-col gap-1 rounded border px-2 pt-1 pb-1 cursor-text transition-all duration-200 border-2 focus-within:shadow-sm ${isDragOver ? 'border-dashed bg-opacity-20' : 'border-transparent'}`, style: {
|
|
3306
3327
|
backgroundColor: theme.colors.surface.primary,
|
|
3307
3328
|
borderColor: isDragOver
|
|
3308
3329
|
? theme.colors.primary[400]
|
|
@@ -3329,7 +3350,7 @@ theme, isOpen, }) {
|
|
|
3329
3350
|
e.currentTarget.style.color = theme.colors.text.tertiary;
|
|
3330
3351
|
}, title: imageSupported
|
|
3331
3352
|
? 'Attach images - AI can analyze image content'
|
|
3332
|
-
: "Current model doesn't support images. Switch to GPT-4O, Claude
|
|
3353
|
+
: "Current model doesn't support images. Switch to GPT-4O, Claude, or Gemini to use image attachments.", tabIndex: -1, type: "button", onClick: () => imageSupported && imageInputRef.current?.click(), disabled: !imageSupported, children: jsx(Icon, { name: "image", size: "sm" }) })), jsx("button", { className: "p-1 cursor-pointer disabled:opacity-50 disabled:cursor-default transition-colors", style: { color: theme.colors.text.tertiary }, title: "Record audio (coming soon)", tabIndex: -1, type: "button", onClick: handleAudioRecord, disabled: true, children: jsx(Icon, { name: "microphone", size: "sm" }) })] }), jsx("button", { onClick: () => onSend(), disabled: (!query.trim() && attachedFiles.length === 0) || isLoading, className: "rounded-full transition disabled:opacity-50 p-2", style: {
|
|
3333
3354
|
color: theme.colors.text.primary,
|
|
3334
3355
|
}, onMouseEnter: (e) => {
|
|
3335
3356
|
if (!e.currentTarget.disabled) {
|
|
@@ -3344,7 +3365,7 @@ theme, isOpen, }) {
|
|
|
3344
3365
|
features.imageAnalysis !== false &&
|
|
3345
3366
|
features.enableImageUploads !== false &&
|
|
3346
3367
|
!imageSupported &&
|
|
3347
|
-
!imageTipDismissed && (jsxs("div", { className: "relative px-2 py-1 bg-amber-50 border border-amber-200 rounded text-amber-700 text-xs flex items-center gap-2", children: [jsx(Icon, { name: "image", size: "xs", className: "opacity-50" }), jsxs("span", { className: "flex-1 pr-4", children: ["\uD83D\uDCA1 ", jsx("strong", { children: "Tip:" }), " Switch to
|
|
3368
|
+
!imageTipDismissed && (jsxs("div", { className: "relative px-2 py-1 bg-amber-50 border border-amber-200 rounded text-amber-700 text-xs flex items-center gap-2", children: [jsx(Icon, { name: "image", size: "xs", className: "opacity-50" }), jsxs("span", { className: "flex-1 pr-4", children: ["\uD83D\uDCA1 ", jsx("strong", { children: "Tip:" }), " Switch to a model showing the image icon in the model switcher below to enable image uploads"] }), jsx("button", { onClick: () => setImageTipDismissed(true), className: "absolute top-1 right-1 w-4 h-4 flex items-center justify-center text-amber-500 hover:text-amber-700 hover:bg-amber-200 rounded-full transition-colors text-xs font-bold", title: "Dismiss tip", type: "button", children: "\u00D7" })] })), jsxs("div", { className: "flex items-center justify-between text-xs px-0 pt-0", style: { color: theme.colors.text.tertiary }, children: [jsx("div", { className: "flex gap-4", children: features.modelSwitching !== false && (jsx(ModelSwitcher, { models: models, defaultModel: defaultModel, showUsageStats: features.usageTracking !== false && showUsageStats !== false, theme: theme, onModelSelectionChange: modelSelectionHandler })) }), jsx("div", { className: "flex items-center gap-2", children: jsxs("span", { children: [jsx("kbd", { className: "px-1 rounded", style: {
|
|
3348
3369
|
backgroundColor: theme.colors.surface.secondary,
|
|
3349
3370
|
color: theme.colors.text.secondary,
|
|
3350
3371
|
}, children: "\u23CE" }), ' ', "to send,", ' ', jsx("kbd", { className: "px-1 rounded", style: {
|
package/dist/index.js
CHANGED
|
@@ -1102,6 +1102,22 @@ function useDebouncedSuggestions(query, options) {
|
|
|
1102
1102
|
};
|
|
1103
1103
|
}
|
|
1104
1104
|
|
|
1105
|
+
// Helper function to build URL from config (reused from useModelSwitcher)
|
|
1106
|
+
function buildUrlFromConfig$1(config) {
|
|
1107
|
+
if (!config)
|
|
1108
|
+
return '';
|
|
1109
|
+
const suffix = config.suffix || '';
|
|
1110
|
+
if (suffix.startsWith('http://') || suffix.startsWith('https://')) {
|
|
1111
|
+
return suffix;
|
|
1112
|
+
}
|
|
1113
|
+
if (typeof window === 'undefined') {
|
|
1114
|
+
return suffix;
|
|
1115
|
+
}
|
|
1116
|
+
const protocol = window.location.protocol === 'https:' ? 'https:' : 'http:';
|
|
1117
|
+
const hostname = window.location.hostname;
|
|
1118
|
+
const port = window.location.port ? `:${window.location.port}` : '';
|
|
1119
|
+
return `${protocol}//${hostname}${port}${suffix}`;
|
|
1120
|
+
}
|
|
1105
1121
|
/**
|
|
1106
1122
|
* Hook to resolve tier-based default model config to actual model
|
|
1107
1123
|
*
|
|
@@ -1111,9 +1127,10 @@ function useDebouncedSuggestions(query, options) {
|
|
|
1111
1127
|
* match for the provider and tier combination.
|
|
1112
1128
|
*
|
|
1113
1129
|
* @param defaultModel - The model configuration to resolve
|
|
1130
|
+
* @param serverConfig - Optional server config to build the models endpoint URL
|
|
1114
1131
|
* @returns The resolved model with provider, model name, and capabilities
|
|
1115
1132
|
*/
|
|
1116
|
-
function useResolvedDefaultModel(defaultModel) {
|
|
1133
|
+
function useResolvedDefaultModel(defaultModel, serverConfig) {
|
|
1117
1134
|
const [resolvedModel, setResolvedModel] = React.useState();
|
|
1118
1135
|
React.useEffect(() => {
|
|
1119
1136
|
if (!defaultModel || defaultModel.model) {
|
|
@@ -1130,7 +1147,11 @@ function useResolvedDefaultModel(defaultModel) {
|
|
|
1130
1147
|
// Need to resolve tier to actual model
|
|
1131
1148
|
const resolveModel = async () => {
|
|
1132
1149
|
try {
|
|
1133
|
-
|
|
1150
|
+
// Build models endpoint URL from serverConfig if provided, otherwise use default
|
|
1151
|
+
const endpoint = serverConfig
|
|
1152
|
+
? `${buildUrlFromConfig$1(serverConfig)}/models`
|
|
1153
|
+
: '/command/models';
|
|
1154
|
+
const response = await fetch(endpoint);
|
|
1134
1155
|
if (!response.ok)
|
|
1135
1156
|
throw new Error('Failed to fetch models');
|
|
1136
1157
|
const data = await response.json();
|
|
@@ -1157,7 +1178,7 @@ function useResolvedDefaultModel(defaultModel) {
|
|
|
1157
1178
|
}
|
|
1158
1179
|
};
|
|
1159
1180
|
resolveModel();
|
|
1160
|
-
}, [defaultModel]);
|
|
1181
|
+
}, [defaultModel, serverConfig]);
|
|
1161
1182
|
return resolvedModel;
|
|
1162
1183
|
}
|
|
1163
1184
|
|
|
@@ -3304,7 +3325,7 @@ theme, isOpen, }) {
|
|
|
3304
3325
|
}, [onModelSelectionChange]);
|
|
3305
3326
|
return (jsxRuntime.jsxs("div", { className: `px-3 py-2 border-t flex flex-col gap-2 transition-all duration-500 delay-500 ${isOpen ? 'opacity-100 translate-y-0' : 'opacity-0 translate-y-4'}`, style: { borderColor: theme.colors.border.primary }, children: [attachedFiles.length > 0 && (jsxRuntime.jsx("div", { className: "flex flex-wrap gap-2 mb-2", children: attachedFiles.map((file, index) => (jsxRuntime.jsxs("div", { className: "relative group", children: [jsxRuntime.jsx("div", { className: "w-16 h-16 rounded-lg border border-gray-200 bg-gray-50 flex items-center justify-center overflow-hidden", children: file.type.startsWith('image/') ? (jsxRuntime.jsx("img", { src: URL.createObjectURL(file), alt: `Attached ${index + 1}`, className: "w-full h-full object-cover" })) : (jsxRuntime.jsx("div", { className: "flex items-center justify-center", children: FileHandler.getFileIcon(file) })) }), jsxRuntime.jsx("button", { onClick: () => removeFile(index), className: "absolute -top-2 -right-2 w-5 h-5 bg-red-500 text-white rounded-full flex items-center justify-center text-xs hover:bg-red-600 transition-colors", title: "Remove file", children: "\u00D7" }), jsxRuntime.jsx("div", { className: "absolute bottom-0 left-1/2 transform -translate-x-1/2 translate-y-full opacity-0 group-hover:opacity-100 transition-opacity duration-200 pointer-events-none z-10 mt-2", children: jsxRuntime.jsxs("div", { className: "bg-gray-900 text-white text-xs rounded-lg px-2 py-1 whitespace-nowrap shadow-lg", children: [jsxRuntime.jsx("div", { className: "font-medium truncate max-w-32", title: file.name, children: file.name }), jsxRuntime.jsxs("div", { className: "text-gray-300", children: [FileHandler.getFileTypeDescription(file), " \u2022", ' ', FileHandler.formatFileSize(file.size)] }), jsxRuntime.jsx("div", { className: "absolute -top-1 left-1/2 transform -translate-x-1/2 w-2 h-2 bg-gray-900 rotate-45" })] }) })] }, index))) })), incompatibleFiles.length > 0 && (jsxRuntime.jsxs("div", { className: "relative flex flex-col gap-1 text-sm text-orange-700 bg-orange-50 border border-orange-200 rounded px-3 py-2 mb-2", children: [jsxRuntime.jsx("button", { onClick: () => setIncompatibleFiles([]), className: "absolute top-1 right-1 w-5 h-5 flex items-center justify-center text-orange-500 hover:text-orange-700 hover:bg-orange-200 rounded-full transition-colors", title: "Dismiss", type: "button", children: "\u00D7" }), jsxRuntime.jsxs("div", { className: "flex items-center gap-2 pr-6", children: [jsxRuntime.jsx("span", { children: "\u26A0\uFE0F" }), jsxRuntime.jsx("span", { className: "font-medium", children: incompatibleFiles.length === 1
|
|
3306
3327
|
? 'File not supported'
|
|
3307
|
-
: 'Some files not supported' })] }), jsxRuntime.jsxs("div", { className: "text-xs", children: [jsxRuntime.jsx("div", { className: "mb-1", children: "These files were skipped:" }), jsxRuntime.jsxs("ul", { className: "list-disc list-inside ml-2 space-y-0.5", children: [incompatibleFiles.slice(0, 3).map((fileName, index) => (jsxRuntime.jsx("li", { className: "truncate", children: fileName }, index))), incompatibleFiles.length > 3 && (jsxRuntime.jsxs("li", { children: ["... and ", incompatibleFiles.length - 3, " more"] }))] }), jsxRuntime.jsxs("div", { className: "mt-1 text-orange-600", children: ["\uD83D\uDCA1 Only ", FileHandler.getAcceptedTypesDescription(), " are supported", incompatibleFiles.some((name) => name.includes('images not supported by current model')) && (jsxRuntime.
|
|
3328
|
+
: 'Some files not supported' })] }), jsxRuntime.jsxs("div", { className: "text-xs", children: [jsxRuntime.jsx("div", { className: "mb-1", children: "These files were skipped:" }), jsxRuntime.jsxs("ul", { className: "list-disc list-inside ml-2 space-y-0.5", children: [incompatibleFiles.slice(0, 3).map((fileName, index) => (jsxRuntime.jsx("li", { className: "truncate", children: fileName }, index))), incompatibleFiles.length > 3 && (jsxRuntime.jsxs("li", { children: ["... and ", incompatibleFiles.length - 3, " more"] }))] }), jsxRuntime.jsxs("div", { className: "mt-1 text-orange-600", children: ["\uD83D\uDCA1 Only ", FileHandler.getAcceptedTypesDescription(), " are supported", incompatibleFiles.some((name) => name.includes('images not supported by current model')) && (jsxRuntime.jsx("div", { className: "mt-1", children: "\uD83D\uDD04 Switch to a model showing the image icon in the model switcher to enable image uploads" }))] })] })] })), jsxRuntime.jsxs("div", { className: `relative flex flex-col gap-1 rounded border px-2 pt-1 pb-1 cursor-text transition-all duration-200 border-2 focus-within:shadow-sm ${isDragOver ? 'border-dashed bg-opacity-20' : 'border-transparent'}`, style: {
|
|
3308
3329
|
backgroundColor: theme.colors.surface.primary,
|
|
3309
3330
|
borderColor: isDragOver
|
|
3310
3331
|
? theme.colors.primary[400]
|
|
@@ -3331,7 +3352,7 @@ theme, isOpen, }) {
|
|
|
3331
3352
|
e.currentTarget.style.color = theme.colors.text.tertiary;
|
|
3332
3353
|
}, title: imageSupported
|
|
3333
3354
|
? 'Attach images - AI can analyze image content'
|
|
3334
|
-
: "Current model doesn't support images. Switch to GPT-4O, Claude
|
|
3355
|
+
: "Current model doesn't support images. Switch to GPT-4O, Claude, or Gemini to use image attachments.", tabIndex: -1, type: "button", onClick: () => imageSupported && imageInputRef.current?.click(), disabled: !imageSupported, children: jsxRuntime.jsx(Icon, { name: "image", size: "sm" }) })), jsxRuntime.jsx("button", { className: "p-1 cursor-pointer disabled:opacity-50 disabled:cursor-default transition-colors", style: { color: theme.colors.text.tertiary }, title: "Record audio (coming soon)", tabIndex: -1, type: "button", onClick: handleAudioRecord, disabled: true, children: jsxRuntime.jsx(Icon, { name: "microphone", size: "sm" }) })] }), jsxRuntime.jsx("button", { onClick: () => onSend(), disabled: (!query.trim() && attachedFiles.length === 0) || isLoading, className: "rounded-full transition disabled:opacity-50 p-2", style: {
|
|
3335
3356
|
color: theme.colors.text.primary,
|
|
3336
3357
|
}, onMouseEnter: (e) => {
|
|
3337
3358
|
if (!e.currentTarget.disabled) {
|
|
@@ -3346,7 +3367,7 @@ theme, isOpen, }) {
|
|
|
3346
3367
|
features.imageAnalysis !== false &&
|
|
3347
3368
|
features.enableImageUploads !== false &&
|
|
3348
3369
|
!imageSupported &&
|
|
3349
|
-
!imageTipDismissed && (jsxRuntime.jsxs("div", { className: "relative px-2 py-1 bg-amber-50 border border-amber-200 rounded text-amber-700 text-xs flex items-center gap-2", children: [jsxRuntime.jsx(Icon, { name: "image", size: "xs", className: "opacity-50" }), jsxRuntime.jsxs("span", { className: "flex-1 pr-4", children: ["\uD83D\uDCA1 ", jsxRuntime.jsx("strong", { children: "Tip:" }), " Switch to
|
|
3370
|
+
!imageTipDismissed && (jsxRuntime.jsxs("div", { className: "relative px-2 py-1 bg-amber-50 border border-amber-200 rounded text-amber-700 text-xs flex items-center gap-2", children: [jsxRuntime.jsx(Icon, { name: "image", size: "xs", className: "opacity-50" }), jsxRuntime.jsxs("span", { className: "flex-1 pr-4", children: ["\uD83D\uDCA1 ", jsxRuntime.jsx("strong", { children: "Tip:" }), " Switch to a model showing the image icon in the model switcher below to enable image uploads"] }), jsxRuntime.jsx("button", { onClick: () => setImageTipDismissed(true), className: "absolute top-1 right-1 w-4 h-4 flex items-center justify-center text-amber-500 hover:text-amber-700 hover:bg-amber-200 rounded-full transition-colors text-xs font-bold", title: "Dismiss tip", type: "button", children: "\u00D7" })] })), jsxRuntime.jsxs("div", { className: "flex items-center justify-between text-xs px-0 pt-0", style: { color: theme.colors.text.tertiary }, children: [jsxRuntime.jsx("div", { className: "flex gap-4", children: features.modelSwitching !== false && (jsxRuntime.jsx(ModelSwitcher, { models: models, defaultModel: defaultModel, showUsageStats: features.usageTracking !== false && showUsageStats !== false, theme: theme, onModelSelectionChange: modelSelectionHandler })) }), jsxRuntime.jsx("div", { className: "flex items-center gap-2", children: jsxRuntime.jsxs("span", { children: [jsxRuntime.jsx("kbd", { className: "px-1 rounded", style: {
|
|
3350
3371
|
backgroundColor: theme.colors.surface.secondary,
|
|
3351
3372
|
color: theme.colors.text.secondary,
|
|
3352
3373
|
}, children: "\u23CE" }), ' ', "to send,", ' ', jsxRuntime.jsx("kbd", { className: "px-1 rounded", style: {
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { ModelCapabilities } from '../types/types';
|
|
2
|
+
import type { ServerConfig } from '../types/types';
|
|
2
3
|
/**
|
|
3
4
|
* Hook to resolve tier-based default model config to actual model
|
|
4
5
|
*
|
|
@@ -8,13 +9,14 @@ import type { ModelCapabilities } from '../types/types';
|
|
|
8
9
|
* match for the provider and tier combination.
|
|
9
10
|
*
|
|
10
11
|
* @param defaultModel - The model configuration to resolve
|
|
12
|
+
* @param serverConfig - Optional server config to build the models endpoint URL
|
|
11
13
|
* @returns The resolved model with provider, model name, and capabilities
|
|
12
14
|
*/
|
|
13
15
|
export declare function useResolvedDefaultModel(defaultModel?: {
|
|
14
16
|
provider: string;
|
|
15
17
|
model?: string;
|
|
16
18
|
tier?: 'fast' | 'balanced' | 'powerful';
|
|
17
|
-
}): {
|
|
19
|
+
}, serverConfig?: ServerConfig): {
|
|
18
20
|
provider: string;
|
|
19
21
|
model: string;
|
|
20
22
|
capabilities: ModelCapabilities;
|
package/dist/server/index.esm.js
CHANGED
|
@@ -8231,6 +8231,10 @@ function sanitizeFormData(formData, schema) {
|
|
|
8231
8231
|
// If schema is provided, use it for validation
|
|
8232
8232
|
if (schema && schema.properties && schema.properties[key]) {
|
|
8233
8233
|
const fieldSchema = schema.properties[key];
|
|
8234
|
+
// Skip client-generated fields - they should not be included in formState
|
|
8235
|
+
if (fieldSchema.skip) {
|
|
8236
|
+
return; // Skip this field
|
|
8237
|
+
}
|
|
8234
8238
|
sanitized[key] = validateAndConvertField(value, fieldSchema);
|
|
8235
8239
|
}
|
|
8236
8240
|
else {
|
|
@@ -13486,6 +13490,7 @@ ${allRoutesInfo}
|
|
|
13486
13490
|
- Match exact field names and types from the chosen schema
|
|
13487
13491
|
- Respect required vs optional fields
|
|
13488
13492
|
- If a field has enum values, only use those exact values
|
|
13493
|
+
- **DO NOT generate fields marked with skip: true** - these are auto-generated on the client side and should never appear in your suggestions
|
|
13489
13494
|
|
|
13490
13495
|
**🚨 CRITICAL CURRENCY FORMATTING:**
|
|
13491
13496
|
- ALL currency amounts (amount, buyRate, sellRate) MUST be NUMBERS in CENTS
|
|
@@ -13565,6 +13570,7 @@ ${this.formatSchemaFields(singleSchema)}${routesInfo}
|
|
|
13565
13570
|
- For nested objects/arrays, follow the nested schema structure
|
|
13566
13571
|
- If a field has enum values, only use those exact values
|
|
13567
13572
|
- When suggesting navigation, use the routes defined in the schema above
|
|
13573
|
+
- **DO NOT generate fields marked with skip: true** - these are auto-generated on the client side and should never appear in your suggestions
|
|
13568
13574
|
|
|
13569
13575
|
**🚨 CRITICAL CURRENCY FORMATTING:**
|
|
13570
13576
|
- ALL currency amounts (amount, buyRate, sellRate) MUST be NUMBERS in CENTS
|
|
@@ -13589,10 +13595,17 @@ ${additionalContext}
|
|
|
13589
13595
|
}
|
|
13590
13596
|
/**
|
|
13591
13597
|
* Format schema fields into readable descriptions
|
|
13598
|
+
* Excludes client-generated fields from the output
|
|
13592
13599
|
*/
|
|
13593
13600
|
formatSchemaFields(schema) {
|
|
13594
13601
|
const fields = [];
|
|
13602
|
+
const clientGeneratedFields = [];
|
|
13595
13603
|
for (const [fieldName, fieldDef] of Object.entries(schema.properties || {})) {
|
|
13604
|
+
// Skip client-generated fields - they should not be shown to AI
|
|
13605
|
+
if (typeof fieldDef === 'object' && fieldDef.skip) {
|
|
13606
|
+
clientGeneratedFields.push(fieldName);
|
|
13607
|
+
continue;
|
|
13608
|
+
}
|
|
13596
13609
|
const required = schema.required?.includes(fieldName) ? '**REQUIRED**' : 'optional';
|
|
13597
13610
|
const type = typeof fieldDef === 'object' ? fieldDef.type || 'unknown' : 'unknown';
|
|
13598
13611
|
const description = typeof fieldDef === 'object' ? fieldDef.description || '' : '';
|
|
@@ -13601,6 +13614,10 @@ ${additionalContext}
|
|
|
13601
13614
|
: '';
|
|
13602
13615
|
fields.push(`- **${fieldName}** (${type}) ${required}${enumValues}${description ? `: ${description}` : ''}`);
|
|
13603
13616
|
}
|
|
13617
|
+
// Add note about client-generated fields if any exist
|
|
13618
|
+
if (clientGeneratedFields.length > 0) {
|
|
13619
|
+
fields.push(`\n**NOTE:** The following fields are auto-generated on the client side and should NOT be included in your suggestions: ${clientGeneratedFields.join(', ')}`);
|
|
13620
|
+
}
|
|
13604
13621
|
return fields.join('\n');
|
|
13605
13622
|
}
|
|
13606
13623
|
/**
|
package/dist/server/index.js
CHANGED
|
@@ -8251,6 +8251,10 @@ function sanitizeFormData(formData, schema) {
|
|
|
8251
8251
|
// If schema is provided, use it for validation
|
|
8252
8252
|
if (schema && schema.properties && schema.properties[key]) {
|
|
8253
8253
|
const fieldSchema = schema.properties[key];
|
|
8254
|
+
// Skip client-generated fields - they should not be included in formState
|
|
8255
|
+
if (fieldSchema.skip) {
|
|
8256
|
+
return; // Skip this field
|
|
8257
|
+
}
|
|
8254
8258
|
sanitized[key] = validateAndConvertField(value, fieldSchema);
|
|
8255
8259
|
}
|
|
8256
8260
|
else {
|
|
@@ -13506,6 +13510,7 @@ ${allRoutesInfo}
|
|
|
13506
13510
|
- Match exact field names and types from the chosen schema
|
|
13507
13511
|
- Respect required vs optional fields
|
|
13508
13512
|
- If a field has enum values, only use those exact values
|
|
13513
|
+
- **DO NOT generate fields marked with skip: true** - these are auto-generated on the client side and should never appear in your suggestions
|
|
13509
13514
|
|
|
13510
13515
|
**🚨 CRITICAL CURRENCY FORMATTING:**
|
|
13511
13516
|
- ALL currency amounts (amount, buyRate, sellRate) MUST be NUMBERS in CENTS
|
|
@@ -13585,6 +13590,7 @@ ${this.formatSchemaFields(singleSchema)}${routesInfo}
|
|
|
13585
13590
|
- For nested objects/arrays, follow the nested schema structure
|
|
13586
13591
|
- If a field has enum values, only use those exact values
|
|
13587
13592
|
- When suggesting navigation, use the routes defined in the schema above
|
|
13593
|
+
- **DO NOT generate fields marked with skip: true** - these are auto-generated on the client side and should never appear in your suggestions
|
|
13588
13594
|
|
|
13589
13595
|
**🚨 CRITICAL CURRENCY FORMATTING:**
|
|
13590
13596
|
- ALL currency amounts (amount, buyRate, sellRate) MUST be NUMBERS in CENTS
|
|
@@ -13609,10 +13615,17 @@ ${additionalContext}
|
|
|
13609
13615
|
}
|
|
13610
13616
|
/**
|
|
13611
13617
|
* Format schema fields into readable descriptions
|
|
13618
|
+
* Excludes client-generated fields from the output
|
|
13612
13619
|
*/
|
|
13613
13620
|
formatSchemaFields(schema) {
|
|
13614
13621
|
const fields = [];
|
|
13622
|
+
const clientGeneratedFields = [];
|
|
13615
13623
|
for (const [fieldName, fieldDef] of Object.entries(schema.properties || {})) {
|
|
13624
|
+
// Skip client-generated fields - they should not be shown to AI
|
|
13625
|
+
if (typeof fieldDef === 'object' && fieldDef.skip) {
|
|
13626
|
+
clientGeneratedFields.push(fieldName);
|
|
13627
|
+
continue;
|
|
13628
|
+
}
|
|
13616
13629
|
const required = schema.required?.includes(fieldName) ? '**REQUIRED**' : 'optional';
|
|
13617
13630
|
const type = typeof fieldDef === 'object' ? fieldDef.type || 'unknown' : 'unknown';
|
|
13618
13631
|
const description = typeof fieldDef === 'object' ? fieldDef.description || '' : '';
|
|
@@ -13621,6 +13634,10 @@ ${additionalContext}
|
|
|
13621
13634
|
: '';
|
|
13622
13635
|
fields.push(`- **${fieldName}** (${type}) ${required}${enumValues}${description ? `: ${description}` : ''}`);
|
|
13623
13636
|
}
|
|
13637
|
+
// Add note about client-generated fields if any exist
|
|
13638
|
+
if (clientGeneratedFields.length > 0) {
|
|
13639
|
+
fields.push(`\n**NOTE:** The following fields are auto-generated on the client side and should NOT be included in your suggestions: ${clientGeneratedFields.join(', ')}`);
|
|
13640
|
+
}
|
|
13624
13641
|
return fields.join('\n');
|
|
13625
13642
|
}
|
|
13626
13643
|
/**
|
|
@@ -134,6 +134,24 @@ export interface ServerConfig {
|
|
|
134
134
|
additionalContext?: string;
|
|
135
135
|
stayOnPage?: boolean;
|
|
136
136
|
}
|
|
137
|
+
export interface ClientConfig {
|
|
138
|
+
models?: Record<string, ModelInfo[]>;
|
|
139
|
+
defaultModel?: {
|
|
140
|
+
provider: string;
|
|
141
|
+
model?: string;
|
|
142
|
+
tier?: 'fast' | 'balanced' | 'powerful';
|
|
143
|
+
};
|
|
144
|
+
showUsageStats?: boolean;
|
|
145
|
+
maxFileSize?: string;
|
|
146
|
+
features?: {
|
|
147
|
+
modelSwitching?: boolean;
|
|
148
|
+
usageTracking?: boolean;
|
|
149
|
+
imageAnalysis?: boolean;
|
|
150
|
+
fileUploads?: boolean;
|
|
151
|
+
enableImageUploads?: boolean;
|
|
152
|
+
};
|
|
153
|
+
schemas?: Record<string, SchemaDefinition>;
|
|
154
|
+
}
|
|
137
155
|
export interface TrainingDataPaths {
|
|
138
156
|
components: string[];
|
|
139
157
|
schemas: string[];
|
|
@@ -465,6 +483,8 @@ export interface SchemaProperty {
|
|
|
465
483
|
maxLength?: number;
|
|
466
484
|
minimum?: number;
|
|
467
485
|
maximum?: number;
|
|
486
|
+
/** If true, this field is not to be generated by AI (e.g. auto-generated fields) */
|
|
487
|
+
skip?: boolean;
|
|
468
488
|
}
|
|
469
489
|
/**
|
|
470
490
|
* Simplified config for ContextualCommandService
|
package/dist/types/types.d.ts
CHANGED
|
@@ -134,6 +134,24 @@ export interface ServerConfig {
|
|
|
134
134
|
additionalContext?: string;
|
|
135
135
|
stayOnPage?: boolean;
|
|
136
136
|
}
|
|
137
|
+
export interface ClientConfig {
|
|
138
|
+
models?: Record<string, ModelInfo[]>;
|
|
139
|
+
defaultModel?: {
|
|
140
|
+
provider: string;
|
|
141
|
+
model?: string;
|
|
142
|
+
tier?: 'fast' | 'balanced' | 'powerful';
|
|
143
|
+
};
|
|
144
|
+
showUsageStats?: boolean;
|
|
145
|
+
maxFileSize?: string;
|
|
146
|
+
features?: {
|
|
147
|
+
modelSwitching?: boolean;
|
|
148
|
+
usageTracking?: boolean;
|
|
149
|
+
imageAnalysis?: boolean;
|
|
150
|
+
fileUploads?: boolean;
|
|
151
|
+
enableImageUploads?: boolean;
|
|
152
|
+
};
|
|
153
|
+
schemas?: Record<string, SchemaDefinition>;
|
|
154
|
+
}
|
|
137
155
|
export interface TrainingDataPaths {
|
|
138
156
|
components: string[];
|
|
139
157
|
schemas: string[];
|
|
@@ -465,6 +483,8 @@ export interface SchemaProperty {
|
|
|
465
483
|
maxLength?: number;
|
|
466
484
|
minimum?: number;
|
|
467
485
|
maximum?: number;
|
|
486
|
+
/** If true, this field is not to be generated by AI (e.g. auto-generated fields) */
|
|
487
|
+
skip?: boolean;
|
|
468
488
|
}
|
|
469
489
|
/**
|
|
470
490
|
* Simplified config for ContextualCommandService
|