Skip to content

Commit 40d2851

Browse files
committed
feat: replace file upload with textarea for AI additional context
- Convert additionalInfo field from file upload to textarea in all datasource forms - Remove file reading logic for additionalInfo content - Add additionalContext prop to all datasource form components - Fix case mismatch in SQLServerForm props (SQLServerDataSource -> sqlServerDataSource) - Update data structure types to include additionalContext This change simplifies the UX by allowing users to directly enter AI context in a textarea instead of having to upload a file.
1 parent 1dd432d commit 40d2851

File tree

15 files changed

+218
-603
lines changed

15 files changed

+218
-603
lines changed

apps/api/src/datasources/structure.ts

+11-205
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,8 @@ import { IOServer } from '../websocket/index.js'
99
import {
1010
DataSourceColumn,
1111
DataSourceStructureError,
12-
dataSourceStructureStateToV2,
13-
dataSourceStructureStateToV3,
14-
DataSourceStructureStateV1,
15-
DataSourceStructureStateV2,
1612
DataSourceStructureStateV3,
1713
DataSourceTable,
18-
jsonString,
1914
parseOrElse,
2015
} from '@briefer/types'
2116
import { config } from '../config/index.js'
@@ -40,199 +35,6 @@ import { z } from 'zod'
4035
import { splitEvery } from 'ramda'
4136
import { createEmbedding } from '../embedding.js'
4237

43-
function decryptDBData(
44-
dataSourceId: string,
45-
type: DataSource['type'],
46-
encrypted: string
47-
) {
48-
try {
49-
return decrypt(encrypted, config().DATASOURCES_ENCRYPTION_KEY)
50-
} catch (err) {
51-
logger().error(
52-
{
53-
err,
54-
dataSourceId,
55-
dataSourceType: type,
56-
},
57-
'Failed to decrypt datasource structure'
58-
)
59-
return null
60-
}
61-
}
62-
63-
async function getV2FromCache(
64-
dataSourceId: string,
65-
type: DataSource['type']
66-
): Promise<DataSourceStructureStateV2 | null> {
67-
let encrypted: string | null
68-
switch (type) {
69-
case 'psql':
70-
encrypted = (
71-
await prisma().postgreSQLDataSource.findUniqueOrThrow({
72-
where: { id: dataSourceId },
73-
select: { structure: true },
74-
})
75-
).structure
76-
break
77-
case 'mysql':
78-
encrypted = (
79-
await prisma().mySQLDataSource.findUniqueOrThrow({
80-
where: { id: dataSourceId },
81-
select: { structure: true },
82-
})
83-
).structure
84-
break
85-
case 'sqlserver':
86-
encrypted = (
87-
await prisma().sQLServerDataSource.findUniqueOrThrow({
88-
where: { id: dataSourceId },
89-
select: { structure: true },
90-
})
91-
).structure
92-
break
93-
case 'trino':
94-
encrypted = (
95-
await prisma().trinoDataSource.findUniqueOrThrow({
96-
where: { id: dataSourceId },
97-
select: { structure: true },
98-
})
99-
).structure
100-
break
101-
case 'athena':
102-
encrypted = (
103-
await prisma().athenaDataSource.findUniqueOrThrow({
104-
where: { id: dataSourceId },
105-
select: { structure: true },
106-
})
107-
).structure
108-
break
109-
case 'oracle':
110-
encrypted = (
111-
await prisma().oracleDataSource.findUniqueOrThrow({
112-
where: { id: dataSourceId },
113-
select: { structure: true },
114-
})
115-
).structure
116-
break
117-
case 'redshift':
118-
encrypted = (
119-
await prisma().redshiftDataSource.findUniqueOrThrow({
120-
where: { id: dataSourceId },
121-
select: { structure: true },
122-
})
123-
).structure
124-
break
125-
case 'bigquery':
126-
encrypted = (
127-
await prisma().bigQueryDataSource.findUniqueOrThrow({
128-
where: { id: dataSourceId },
129-
select: { structure: true },
130-
})
131-
).structure
132-
break
133-
case 'snowflake':
134-
encrypted = (
135-
await prisma().snowflakeDataSource.findUniqueOrThrow({
136-
where: { id: dataSourceId },
137-
select: { structure: true },
138-
})
139-
).structure
140-
break
141-
case 'databrickssql':
142-
encrypted = (
143-
await prisma().databricksSQLDataSource.findUniqueOrThrow({
144-
where: { id: dataSourceId },
145-
select: { structure: true },
146-
})
147-
).structure
148-
break
149-
}
150-
151-
if (encrypted === null) {
152-
return null
153-
}
154-
155-
const decrypted = decryptDBData(dataSourceId, type, encrypted)
156-
if (decrypted === null) {
157-
return null
158-
}
159-
160-
const parsed = jsonString
161-
.pipe(z.union([DataSourceStructureStateV1, DataSourceStructureStateV2]))
162-
.safeParse(decrypted)
163-
if (!parsed.success) {
164-
logger().error(
165-
{
166-
dataSourceId,
167-
dataSourceType: type,
168-
err: parsed.error,
169-
},
170-
'Failed to parse datasource structure from database'
171-
)
172-
return null
173-
}
174-
175-
return dataSourceStructureStateToV2(parsed.data)
176-
}
177-
178-
async function v2ToV3(
179-
dataSourceId: string,
180-
type: DataSource['type'],
181-
v2: DataSourceStructureStateV2 | null
182-
): Promise<DataSourceStructureStateV3 | null> {
183-
if (!v2) {
184-
return null
185-
}
186-
187-
const refreshPing: Date | null = (() => {
188-
switch (v2.status) {
189-
case 'success': {
190-
if (v2.refreshPing) {
191-
return new Date(v2.refreshPing)
192-
}
193-
return null
194-
}
195-
case 'loading':
196-
return new Date(v2.loadingPing)
197-
case 'failed':
198-
return null
199-
}
200-
})()
201-
202-
const dbSchema = await prisma().dataSourceSchema.create({
203-
data: {
204-
status: v2.status,
205-
refreshPing,
206-
startedAt: 'startedAt' in v2 ? new Date(v2.startedAt) : null,
207-
finishedAt: 'updatedAt' in v2 ? new Date(v2.updatedAt) : null,
208-
failedAt: 'failedAt' in v2 ? new Date(v2.failedAt) : null,
209-
error: 'error' in v2 ? v2.error : undefined,
210-
defaultSchema:
211-
'structure' in v2 ? v2.structure?.defaultSchema ?? null : null,
212-
},
213-
})
214-
215-
if ('structure' in v2 && v2.structure) {
216-
await prisma().dataSourceSchemaTable.createMany({
217-
data: Object.entries(v2.structure.schemas).flatMap(([schema, tables]) => {
218-
return Object.entries(tables.tables).map(([tableName, table]) => {
219-
return {
220-
schema,
221-
name: tableName,
222-
columns: table.columns,
223-
dataSourceSchemaId: dbSchema.id,
224-
embeddingModel: '',
225-
}
226-
})
227-
}),
228-
})
229-
}
230-
231-
await assignDataSourceSchemaId(dataSourceId, type, dbSchema)
232-
233-
return dataSourceStructureStateToV3(dbSchema.id, v2)
234-
}
235-
23638
async function assignDataSourceSchemaId(
23739
dataSourceId: string,
23840
type: DataSource['type'],
@@ -394,11 +196,7 @@ export async function fetchDataSourceStructureFromCache(
394196
}
395197

396198
if (schema === null) {
397-
return await v2ToV3(
398-
dataSourceId,
399-
type,
400-
await getV2FromCache(dataSourceId, type)
401-
)
199+
return null
402200
}
403201

404202
switch (schema.status) {
@@ -412,6 +210,7 @@ export async function fetchDataSourceStructureFromCache(
412210
status: 'loading',
413211
startedAt: schema.startedAt.getTime(),
414212
loadingPing: schema.refreshPing.getTime(),
213+
additionalContext: schema.additionalInfo,
415214
version: 3,
416215
}
417216
}
@@ -430,8 +229,9 @@ export async function fetchDataSourceStructureFromCache(
430229
failedAt: schema.failedAt.getTime(),
431230
previousSuccessAt: schema.finishedAt?.getTime() ?? null,
432231
error,
433-
version: 3,
434232
defaultSchema: schema.defaultSchema,
233+
additionalContext: schema.additionalInfo,
234+
version: 3,
435235
}
436236
}
437237
case 'success': {
@@ -445,6 +245,7 @@ export async function fetchDataSourceStructureFromCache(
445245
updatedAt: schema.finishedAt.getTime(),
446246
refreshPing: schema.refreshPing?.getTime() ?? null,
447247
defaultSchema: schema.defaultSchema,
248+
additionalContext: schema.additionalInfo,
448249
version: 3,
449250
}
450251
}
@@ -497,6 +298,7 @@ export async function fetchDataSourceStructure(
497298
where: { id: structure.id },
498299
data: { additionalInfo },
499300
})
301+
structure.additionalContext = additionalInfo
500302
}
501303

502304
return structure
@@ -687,6 +489,7 @@ async function _refreshDataSourceStructure(
687489
updatedAt: Date.now(),
688490
refreshPing: null,
689491
defaultSchema,
492+
additionalContext: dataSource.structure.additionalContext,
690493
version: 3,
691494
})
692495

@@ -750,11 +553,12 @@ async function _refreshDataSourceStructure(
750553
? dataSource.structure.updatedAt
751554
: null,
752555
error,
753-
version: 3,
556+
additionalContext: dataSource.structure.additionalContext,
754557
defaultSchema:
755558
'defaultSchema' in dataSource.structure
756559
? dataSource.structure.defaultSchema
757560
: null,
561+
version: 3,
758562
})
759563
broadcastDataSource(socketServer, dataSource)
760564
}
@@ -781,6 +585,7 @@ async function updateToLoading(
781585
status: 'loading',
782586
startedAt: now.getTime(),
783587
loadingPing: now.getTime(),
588+
additionalContext: null,
784589
version: 3,
785590
},
786591
}
@@ -794,6 +599,7 @@ async function updateToLoading(
794599
status: 'loading',
795600
startedAt: Date.now(),
796601
loadingPing: Date.now(),
602+
additionalContext: currentStructure.additionalContext,
797603
version: 3,
798604
}
799605
break

apps/api/src/v1/workspaces/workspace/data-sources/data-source.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ import prisma, {
3434
updateDatabricksSQLDataSource,
3535
deleteDatabricksSQLDataSource,
3636
listDataSources,
37-
DataSource,
3837
} from '@briefer/database'
3938
import { z } from 'zod'
4039
import { getParam } from '../../../../utils/express.js'
@@ -363,6 +362,8 @@ const dataSourceRouter = (socketServer: IOServer) => {
363362
config: ds,
364363
structure,
365364
})
365+
366+
broadcastDataSource(socketServer, result)
366367
res.json(result)
367368
})
368369

apps/api/src/v1/workspaces/workspace/data-sources/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -390,6 +390,7 @@ const dataSourcesRouter = (socketServer: IOServer) => {
390390
status: 'loading',
391391
startedAt: Date.now(),
392392
loadingPing: 0,
393+
additionalContext: data.data.additionalInfo ?? null,
393394
version: 3,
394395
},
395396
})

0 commit comments

Comments
 (0)