mirror of
https://github.com/simstudioai/sim.git
synced 2026-04-06 03:00:16 -04:00
Update oauth cred tool
This commit is contained in:
@@ -28,6 +28,110 @@ const logger = createLogger('CopilotSseToolExecution')
|
||||
|
||||
const OUTPUT_PATH_TOOLS = new Set(['function_execute', 'user_table'])
|
||||
|
||||
/**
|
||||
* Try to pull a flat array of row-objects out of the various shapes that
|
||||
* `function_execute` and `user_table` can return.
|
||||
*/
|
||||
function extractTabularData(output: unknown): Record<string, unknown>[] | null {
|
||||
if (!output || typeof output !== 'object') return null
|
||||
|
||||
if (Array.isArray(output)) {
|
||||
if (output.length > 0 && typeof output[0] === 'object' && output[0] !== null) {
|
||||
return output as Record<string, unknown>[]
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
const obj = output as Record<string, unknown>
|
||||
|
||||
// function_execute shape: { result: [...], stdout: "..." }
|
||||
if (Array.isArray(obj.result)) {
|
||||
const rows = obj.result
|
||||
if (rows.length > 0 && typeof rows[0] === 'object' && rows[0] !== null) {
|
||||
return rows as Record<string, unknown>[]
|
||||
}
|
||||
}
|
||||
|
||||
// user_table query_rows shape: { data: { rows: [{ data: {...} }], totalCount } }
|
||||
if (obj.data && typeof obj.data === 'object' && !Array.isArray(obj.data)) {
|
||||
const data = obj.data as Record<string, unknown>
|
||||
if (Array.isArray(data.rows) && data.rows.length > 0) {
|
||||
const rows = data.rows as Record<string, unknown>[]
|
||||
// user_table rows nest actual values inside .data
|
||||
if (typeof rows[0].data === 'object' && rows[0].data !== null) {
|
||||
return rows.map((r) => r.data as Record<string, unknown>)
|
||||
}
|
||||
return rows
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
function escapeCsvValue(value: unknown): string {
|
||||
if (value === null || value === undefined) return ''
|
||||
const str = typeof value === 'object' ? JSON.stringify(value) : String(value)
|
||||
if (str.includes(',') || str.includes('"') || str.includes('\n') || str.includes('\r')) {
|
||||
return `"${str.replace(/"/g, '""')}"`
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
function convertRowsToCsv(rows: Record<string, unknown>[]): string {
|
||||
if (rows.length === 0) return ''
|
||||
|
||||
const headerSet = new Set<string>()
|
||||
for (const row of rows) {
|
||||
for (const key of Object.keys(row)) {
|
||||
headerSet.add(key)
|
||||
}
|
||||
}
|
||||
const headers = [...headerSet]
|
||||
|
||||
const lines = [headers.map(escapeCsvValue).join(',')]
|
||||
for (const row of rows) {
|
||||
lines.push(headers.map((h) => escapeCsvValue(row[h])).join(','))
|
||||
}
|
||||
return lines.join('\n')
|
||||
}
|
||||
|
||||
type OutputFormat = 'json' | 'csv' | 'txt' | 'md' | 'html'
|
||||
|
||||
const EXT_TO_FORMAT: Record<string, OutputFormat> = {
|
||||
'.json': 'json',
|
||||
'.csv': 'csv',
|
||||
'.txt': 'txt',
|
||||
'.md': 'md',
|
||||
'.html': 'html',
|
||||
}
|
||||
|
||||
const FORMAT_TO_CONTENT_TYPE: Record<OutputFormat, string> = {
|
||||
json: 'application/json',
|
||||
csv: 'text/csv',
|
||||
txt: 'text/plain',
|
||||
md: 'text/markdown',
|
||||
html: 'text/html',
|
||||
}
|
||||
|
||||
function resolveOutputFormat(fileName: string, explicit?: string): OutputFormat {
|
||||
if (explicit && explicit in FORMAT_TO_CONTENT_TYPE) return explicit as OutputFormat
|
||||
const ext = fileName.slice(fileName.lastIndexOf('.')).toLowerCase()
|
||||
return EXT_TO_FORMAT[ext] ?? 'json'
|
||||
}
|
||||
|
||||
function serializeOutputForFile(output: unknown, format: OutputFormat): string {
|
||||
if (typeof output === 'string') return output
|
||||
|
||||
if (format === 'csv') {
|
||||
const rows = extractTabularData(output)
|
||||
if (rows && rows.length > 0) {
|
||||
return convertRowsToCsv(rows)
|
||||
}
|
||||
}
|
||||
|
||||
return JSON.stringify(output, null, 2)
|
||||
}
|
||||
|
||||
async function maybeWriteOutputToFile(
|
||||
toolName: string,
|
||||
params: Record<string, unknown> | undefined,
|
||||
@@ -38,30 +142,19 @@ async function maybeWriteOutputToFile(
|
||||
if (!OUTPUT_PATH_TOOLS.has(toolName)) return result
|
||||
if (!context.workspaceId || !context.userId) return result
|
||||
|
||||
const args = params?.args as Record<string, unknown> | undefined
|
||||
const outputPath =
|
||||
(params?.outputPath as string | undefined) ??
|
||||
((params?.args as Record<string, unknown> | undefined)?.outputPath as string | undefined)
|
||||
(params?.outputPath as string | undefined) ?? (args?.outputPath as string | undefined)
|
||||
if (!outputPath) return result
|
||||
|
||||
const explicitFormat =
|
||||
(params?.outputFormat as string | undefined) ?? (args?.outputFormat as string | undefined)
|
||||
const fileName = outputPath.replace(/^files\//, '')
|
||||
const format = resolveOutputFormat(fileName, explicitFormat)
|
||||
|
||||
try {
|
||||
let content: string
|
||||
if (typeof result.output === 'string') {
|
||||
content = result.output
|
||||
} else {
|
||||
content = JSON.stringify(result.output, null, 2)
|
||||
}
|
||||
|
||||
const contentType = fileName.endsWith('.json')
|
||||
? 'application/json'
|
||||
: fileName.endsWith('.csv')
|
||||
? 'text/csv'
|
||||
: fileName.endsWith('.md')
|
||||
? 'text/markdown'
|
||||
: fileName.endsWith('.html')
|
||||
? 'text/html'
|
||||
: 'text/plain'
|
||||
const content = serializeOutputForFile(result.output, format)
|
||||
const contentType = FORMAT_TO_CONTENT_TYPE[format]
|
||||
|
||||
const buffer = Buffer.from(content, 'utf-8')
|
||||
const uploaded = await uploadWorkspaceFile(
|
||||
|
||||
Reference in New Issue
Block a user