This commit is contained in:
Lakee Sivaraya
2026-01-15 11:39:01 -08:00
parent c3afbaebce
commit 5a69d16e65
5 changed files with 129 additions and 21 deletions

View File

@@ -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),

View File

@@ -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,
]
)

View File

@@ -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',
},
},
{

View File

@@ -26,6 +26,7 @@ export type GenerationType =
| 'typescript-function-body'
| 'json-schema'
| 'json-object'
| 'table-schema'
| 'system-prompt'
| 'custom-tool-schema'
| 'sql-query'

View 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
}
}