mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-28 03:00:29 -04:00
wand
This commit is contained in:
@@ -244,6 +244,7 @@ export function Code({
|
||||
case 'json-schema':
|
||||
return 'Describe the JSON schema to generate...'
|
||||
case 'json-object':
|
||||
case 'table-schema':
|
||||
return 'Describe the JSON object to generate...'
|
||||
default:
|
||||
return 'Describe the JavaScript code to generate...'
|
||||
@@ -268,9 +269,14 @@ export function Code({
|
||||
return wandConfig
|
||||
}, [wandConfig, languageValue])
|
||||
|
||||
const [tableIdValue] = useSubBlockValue<string>(blockId, 'tableId')
|
||||
|
||||
const wandHook = useWand({
|
||||
wandConfig: dynamicWandConfig || { enabled: false, prompt: '' },
|
||||
currentValue: code,
|
||||
contextParams: {
|
||||
tableId: typeof tableIdValue === 'string' ? tableIdValue : null,
|
||||
},
|
||||
onStreamStart: () => handleStreamStartRef.current?.(),
|
||||
onStreamChunk: (chunk: string) => handleStreamChunkRef.current?.(chunk),
|
||||
onGeneratedContent: (content: string) => handleGeneratedContentRef.current?.(content),
|
||||
|
||||
@@ -1,25 +1,43 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useQueryClient } from '@tanstack/react-query'
|
||||
import { fetchTableSchemaContext } from '@/lib/table/schema-context'
|
||||
import type { GenerationType } from '@/blocks/types'
|
||||
import { subscriptionKeys } from '@/hooks/queries/subscription'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('useWand')
|
||||
|
||||
interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
content: string
|
||||
}
|
||||
|
||||
interface BuildWandContextInfoOptions {
|
||||
currentValue?: string
|
||||
generationType?: string
|
||||
tableId?: string | null
|
||||
workspaceId?: string | null
|
||||
}
|
||||
|
||||
/**
|
||||
* Builds rich context information based on current content and generation type
|
||||
* Builds rich context information based on current content and generation type.
|
||||
*/
|
||||
function buildContextInfo(currentValue?: string, generationType?: string): string {
|
||||
if (!currentValue || currentValue.trim() === '') {
|
||||
return 'no current content'
|
||||
}
|
||||
async function buildWandContextInfo({
|
||||
currentValue,
|
||||
generationType,
|
||||
tableId,
|
||||
workspaceId,
|
||||
}: BuildWandContextInfoOptions): Promise<string> {
|
||||
const hasContent = Boolean(currentValue && currentValue.trim() !== '')
|
||||
const contentLength = currentValue?.length ?? 0
|
||||
const lineCount = currentValue ? currentValue.split('\n').length : 0
|
||||
|
||||
const contentLength = currentValue.length
|
||||
const lineCount = currentValue.split('\n').length
|
||||
let contextInfo = hasContent
|
||||
? `Current content (${contentLength} characters, ${lineCount} lines):\n${currentValue}`
|
||||
: 'no current content'
|
||||
|
||||
let contextInfo = `Current content (${contentLength} characters, ${lineCount} lines):\n${currentValue}`
|
||||
|
||||
if (generationType) {
|
||||
if (generationType && currentValue) {
|
||||
switch (generationType) {
|
||||
case 'javascript-function-body':
|
||||
case 'typescript-function-body': {
|
||||
@@ -32,6 +50,7 @@ function buildContextInfo(currentValue?: string, generationType?: string): strin
|
||||
|
||||
case 'json-schema':
|
||||
case 'json-object':
|
||||
case 'table-schema':
|
||||
try {
|
||||
const parsed = JSON.parse(currentValue)
|
||||
const keys = Object.keys(parsed)
|
||||
@@ -43,12 +62,14 @@ function buildContextInfo(currentValue?: string, generationType?: string): strin
|
||||
}
|
||||
}
|
||||
|
||||
return contextInfo
|
||||
}
|
||||
if (generationType === 'table-schema') {
|
||||
const tableSchemaContext = await fetchTableSchemaContext({ tableId, workspaceId })
|
||||
if (tableSchemaContext) {
|
||||
contextInfo += `\n\n${tableSchemaContext}`
|
||||
}
|
||||
}
|
||||
|
||||
interface ChatMessage {
|
||||
role: 'user' | 'assistant' | 'system'
|
||||
content: string
|
||||
return contextInfo
|
||||
}
|
||||
|
||||
export interface WandConfig {
|
||||
@@ -62,6 +83,9 @@ export interface WandConfig {
|
||||
interface UseWandProps {
|
||||
wandConfig?: WandConfig
|
||||
currentValue?: string
|
||||
contextParams?: {
|
||||
tableId?: string | null
|
||||
}
|
||||
onGeneratedContent: (content: string) => void
|
||||
onStreamChunk?: (chunk: string) => void
|
||||
onStreamStart?: () => void
|
||||
@@ -71,12 +95,14 @@ interface UseWandProps {
|
||||
export function useWand({
|
||||
wandConfig,
|
||||
currentValue,
|
||||
contextParams,
|
||||
onGeneratedContent,
|
||||
onStreamChunk,
|
||||
onStreamStart,
|
||||
onGenerationComplete,
|
||||
}: UseWandProps) {
|
||||
const queryClient = useQueryClient()
|
||||
const workspaceId = useWorkflowRegistry((state) => state.hydration.workspaceId)
|
||||
const [isLoading, setIsLoading] = useState(false)
|
||||
const [isPromptVisible, setIsPromptVisible] = useState(false)
|
||||
const [promptInputValue, setPromptInputValue] = useState('')
|
||||
@@ -147,7 +173,12 @@ export function useWand({
|
||||
}
|
||||
|
||||
try {
|
||||
const contextInfo = buildContextInfo(currentValue, wandConfig?.generationType)
|
||||
const contextInfo = await buildWandContextInfo({
|
||||
currentValue,
|
||||
generationType: wandConfig?.generationType,
|
||||
tableId: contextParams?.tableId,
|
||||
workspaceId,
|
||||
})
|
||||
|
||||
let systemPrompt = wandConfig?.prompt || ''
|
||||
if (systemPrompt.includes('{context}')) {
|
||||
@@ -276,6 +307,8 @@ export function useWand({
|
||||
onStreamStart,
|
||||
onGenerationComplete,
|
||||
queryClient,
|
||||
contextParams?.tableId,
|
||||
workspaceId,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -87,7 +87,7 @@ Table with columns: customer_id (string), total (number), status (string)
|
||||
→ {"customer_id": "123", "total": 99.99, "status": "pending"}
|
||||
|
||||
Return ONLY the data JSON:`,
|
||||
generationType: 'json-object',
|
||||
generationType: 'table-schema',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -124,7 +124,7 @@ Table with columns: email (string), name (string), age (number)
|
||||
]
|
||||
|
||||
Return ONLY the rows array:`,
|
||||
generationType: 'json-object',
|
||||
generationType: 'table-schema',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -212,7 +212,7 @@ IMPORTANT: Reference the table schema to know which columns exist and their type
|
||||
→ {"email": {"$contains": "gmail.com"}}
|
||||
|
||||
Return ONLY the filter JSON:`,
|
||||
generationType: 'json-object',
|
||||
generationType: 'table-schema',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -320,7 +320,7 @@ Table with columns: status (string), age (number), email (string), active (boole
|
||||
→ {"email": {"$contains": "example.com"}}
|
||||
|
||||
Return ONLY the filter JSON:`,
|
||||
generationType: 'json-object',
|
||||
generationType: 'table-schema',
|
||||
},
|
||||
},
|
||||
|
||||
@@ -373,7 +373,7 @@ Table with columns: name (string), age (number), email (string), createdAt (date
|
||||
→ {"createdAt": "asc"}
|
||||
|
||||
Return ONLY the sort JSON:`,
|
||||
generationType: 'json-object',
|
||||
generationType: 'table-schema',
|
||||
},
|
||||
},
|
||||
{
|
||||
|
||||
@@ -26,6 +26,7 @@ export type GenerationType =
|
||||
| 'typescript-function-body'
|
||||
| 'json-schema'
|
||||
| 'json-object'
|
||||
| 'table-schema'
|
||||
| 'system-prompt'
|
||||
| 'custom-tool-schema'
|
||||
| 'sql-query'
|
||||
|
||||
68
apps/sim/lib/table/schema-context.ts
Normal file
68
apps/sim/lib/table/schema-context.ts
Normal file
@@ -0,0 +1,68 @@
|
||||
import { createLogger } from '@sim/logger'
|
||||
import type { TableSchema } from '@/lib/table/types'
|
||||
|
||||
const logger = createLogger('TableSchemaContext')
|
||||
|
||||
interface TableSchemaResponse {
|
||||
data?: {
|
||||
table?: {
|
||||
id: string
|
||||
name?: string | null
|
||||
schema?: TableSchema
|
||||
}
|
||||
}
|
||||
table?: {
|
||||
id: string
|
||||
name?: string | null
|
||||
schema?: TableSchema
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches table schema context for wand generation.
|
||||
*/
|
||||
export async function fetchTableSchemaContext({
|
||||
tableId,
|
||||
workspaceId,
|
||||
}: {
|
||||
tableId?: string | null
|
||||
workspaceId?: string | null
|
||||
}): Promise<string | null> {
|
||||
if (!tableId || !workspaceId) {
|
||||
return null
|
||||
}
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/table/${tableId}?workspaceId=${workspaceId}`)
|
||||
if (!response.ok) {
|
||||
return null
|
||||
}
|
||||
|
||||
const result = (await response.json()) as TableSchemaResponse
|
||||
const table = result.data?.table ?? result.table
|
||||
const schema = table?.schema
|
||||
|
||||
if (!table || !schema || !schema.columns || schema.columns.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
const columnLines = schema.columns
|
||||
.map((column) => {
|
||||
const flags = [
|
||||
column.type,
|
||||
column.required ? 'required' : null,
|
||||
column.unique ? 'unique' : null,
|
||||
].filter(Boolean)
|
||||
const descriptor = flags.length ? ` (${flags.join(', ')})` : ''
|
||||
return `- ${column.name}${descriptor}`
|
||||
})
|
||||
.join('\n')
|
||||
|
||||
const tableLabel = table.name ? `${table.name} (${table.id})` : table.id
|
||||
|
||||
return `Table schema for ${tableLabel}:\n${columnLines}\nBuilt-in columns: createdAt, updatedAt`
|
||||
} catch (error) {
|
||||
logger.debug('Failed to fetch table schema for wand context', { tableId, error })
|
||||
return null
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user