Skip to content

Commit b10c356

Browse files
committed
fix(tinyfish): address review findings
- Add tinyfish to PROVIDER_SECTIONS. The sectioned BYOK renderer drops any provider missing from a section, so the key field never rendered. Export PROVIDERS/PROVIDER_SECTIONS and assert they agree, so the next provider to miss a section fails CI instead of vanishing. - Replace the `any` payload params with raw snake_case wire types. This caught two real gaps: `status` could reach the output undefined, and `error` could be null where ToolResponse.error is `string | undefined`. - Reject an already-parsed array output schema, which a `json` block input can produce, and name malformed schema JSON instead of leaking a SyntaxError. - Count Fetch rate-limit usage from the submitted URLs rather than the returned arrays, so a URL in neither array cannot undercount. - Make "List runs" a literal canvas clause; a blank goal filter lists everything. - Regenerate the docs manifest for the new integration page.
1 parent 4ada9e8 commit b10c356

15 files changed

Lines changed: 299 additions & 103 deletions

File tree

apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.test.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,27 @@ vi.mock('@/hooks/queries/byok-keys', () => ({
171171
useDeleteOrganizationBYOKKey: mocks.mutation,
172172
}))
173173

174-
import { BYOK } from '@/app/workspace/[workspaceId]/settings/components/byok/byok'
174+
import {
175+
BYOK,
176+
PROVIDER_SECTIONS,
177+
PROVIDERS,
178+
} from '@/app/workspace/[workspaceId]/settings/components/byok/byok'
179+
180+
describe('BYOK provider sections', () => {
181+
it('renders every provider, since the sectioned list drops unlisted ids', () => {
182+
const sectioned = new Set(PROVIDER_SECTIONS.flatMap((section) => section.ids))
183+
const missing = PROVIDERS.map((provider) => provider.id).filter((id) => !sectioned.has(id))
184+
expect(missing).toEqual([])
185+
})
186+
187+
it('has no section entry without a matching provider', () => {
188+
const known = new Set(PROVIDERS.map((provider) => provider.id))
189+
const orphaned = PROVIDER_SECTIONS.flatMap((section) => section.ids).filter(
190+
(id) => !known.has(id)
191+
)
192+
expect(orphaned).toEqual([])
193+
})
194+
})
175195

176196
describe('BYOK scope access', () => {
177197
let container: HTMLDivElement

apps/sim/app/workspace/[workspaceId]/settings/components/byok/byok.tsx

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,12 @@ import {
7171
useUpsertOrganizationBYOKKey,
7272
} from '@/hooks/queries/byok-keys'
7373

74-
const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
74+
/**
75+
* Every provider the BYOK page can render. A provider listed here but missing
76+
* from {@link PROVIDER_SECTIONS} is dropped by the sectioned renderer, so the
77+
* two lists are kept in sync by a test rather than by memory.
78+
*/
79+
export const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
7580
{
7681
id: 'openai',
7782
name: 'OpenAI',
@@ -338,7 +343,7 @@ const PROVIDERS: (BYOKManagerProvider & { id: BYOKProviderId })[] = [
338343
* {@link PROVIDERS} belongs to exactly one section; rows keep their
339344
* {@link PROVIDERS} order within each group.
340345
*/
341-
const PROVIDER_SECTIONS: BYOKProviderSection[] = [
346+
export const PROVIDER_SECTIONS: BYOKProviderSection[] = [
342347
{
343348
label: 'Models',
344349
ids: [
@@ -363,6 +368,7 @@ const PROVIDER_SECTIONS: BYOKProviderSection[] = [
363368
'firecrawl',
364369
'exa',
365370
'context_dev',
371+
'tinyfish',
366372
'serper',
367373
'linkup',
368374
'parallel_ai',

apps/sim/blocks/blocks/tinyfish.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,8 @@ export const TinyFishBlock: BlockConfig<TinyFishRunResponse> = {
5555
tinyfish_get_run: [{ text: 'Get run', field: 'runId', core: true }],
5656
tinyfish_cancel_run: [{ text: 'Cancel run', field: 'runId', core: true }],
5757
tinyfish_list_runs: [
58-
{ text: 'List runs matching', field: 'goalFilter', core: true },
58+
'List runs',
59+
{ text: 'matching', field: 'goalFilter' },
5960
{ text: ', with status', field: 'status' },
6061
],
6162
tinyfish_search: [{ text: 'Search the web for', field: 'query', core: true }],

apps/sim/lib/copilot/generated/docs-manifest.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,6 +314,7 @@ export const DOCS_MANIFEST: readonly string[] = [
314314
'integrations/thrive.mdx',
315315
'integrations/tiktok.mdx',
316316
'integrations/tinybird.mdx',
317+
'integrations/tinyfish.mdx',
317318
'integrations/trello-service-account.mdx',
318319
'integrations/trello.mdx',
319320
'integrations/trigger_dev.mdx',

apps/sim/tools/tinyfish/cancel_run.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import type { TinyFishCancelRunParams, TinyFishCancelRunResponse } from '@/tools/tinyfish/types'
1+
import type {
2+
TinyFishCancelRunParams,
3+
TinyFishCancelRunResponse,
4+
TinyFishRawCancel,
5+
} from '@/tools/tinyfish/types'
26
import {
37
TINYFISH_AGENT_API_BASE,
48
tinyfishErrorMessage,
@@ -45,13 +49,13 @@ export const cancelRunTool: ToolConfig<TinyFishCancelRunParams, TinyFishCancelRu
4549
throw new Error(await tinyfishErrorMessage(response))
4650
}
4751

48-
const data = await response.json()
52+
const data = (await response.json()) as TinyFishRawCancel
4953

5054
return {
5155
success: true,
5256
output: {
5357
runId: data.run_id ?? '',
54-
status: data.status,
58+
status: data.status ?? 'CANCELLED',
5559
cancelledAt: data.cancelled_at ?? null,
5660
message: data.message ?? null,
5761
},

apps/sim/tools/tinyfish/fetch_urls.ts

Lines changed: 24 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import { tinyfishFetchHosting } from '@/tools/tinyfish/hosting'
2-
import type { TinyFishFetchParams, TinyFishFetchResponse } from '@/tools/tinyfish/types'
2+
import type {
3+
TinyFishFetchParams,
4+
TinyFishFetchResponse,
5+
TinyFishRawFetch,
6+
} from '@/tools/tinyfish/types'
37
import {
48
MAX_FETCH_URLS,
59
parseList,
@@ -81,33 +85,29 @@ export const fetchUrlsTool: ToolConfig<TinyFishFetchParams, TinyFishFetchRespons
8185
throw new Error(await tinyfishErrorMessage(response))
8286
}
8387

84-
const data = await response.json()
88+
const data = (await response.json()) as TinyFishRawFetch
8589

8690
return {
8791
success: true,
8892
output: {
89-
results: Array.isArray(data.results)
90-
? data.results.map((result: any) => ({
91-
url: result?.url ?? '',
92-
finalUrl: result?.final_url ?? null,
93-
title: result?.title ?? null,
94-
description: result?.description ?? null,
95-
language: result?.language ?? null,
96-
format: result?.format ?? 'markdown',
97-
text: result?.text ?? null,
98-
author: result?.author ?? null,
99-
publishedDate: result?.published_date ?? null,
100-
links: result?.links ?? [],
101-
imageLinks: result?.image_links ?? [],
102-
latencyMs: result?.latency_ms ?? null,
103-
}))
104-
: [],
105-
errors: Array.isArray(data.errors)
106-
? data.errors.map((issue: any) => ({
107-
url: issue?.url ?? '',
108-
error: issue?.error ?? '',
109-
}))
110-
: [],
93+
results: (data.results ?? []).map((result) => ({
94+
url: result?.url ?? '',
95+
finalUrl: result?.final_url ?? null,
96+
title: result?.title ?? null,
97+
description: result?.description ?? null,
98+
language: result?.language ?? null,
99+
format: result?.format ?? 'markdown',
100+
text: result?.text ?? null,
101+
author: result?.author ?? null,
102+
publishedDate: result?.published_date ?? null,
103+
links: result?.links ?? [],
104+
imageLinks: result?.image_links ?? [],
105+
latencyMs: result?.latency_ms ?? null,
106+
})),
107+
errors: (data.errors ?? []).map((issue) => ({
108+
url: issue?.url ?? '',
109+
error: issue?.error ?? '',
110+
})),
111111
},
112112
}
113113
},

apps/sim/tools/tinyfish/get_run.ts

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
RUN_SUMMARY_OUTPUT_PROPERTIES,
33
type TinyFishGetRunParams,
44
type TinyFishGetRunResponse,
5+
type TinyFishRawRunDetail,
56
} from '@/tools/tinyfish/types'
67
import {
78
mapRunSummary,
@@ -45,22 +46,20 @@ export const getRunTool: ToolConfig<TinyFishGetRunParams, TinyFishGetRunResponse
4546
throw new Error(await tinyfishErrorMessage(response))
4647
}
4748

48-
const data = await response.json()
49+
const data = (await response.json()) as TinyFishRawRunDetail
4950

5051
return {
5152
success: true,
5253
output: {
5354
...mapRunSummary(data),
5455
videoUrl: data.video_url ?? null,
55-
steps: Array.isArray(data.steps)
56-
? data.steps.map((step: any) => ({
57-
id: step?.id ?? '',
58-
timestamp: step?.timestamp ?? '',
59-
status: step?.status ?? 'PENDING',
60-
action: step?.action ?? null,
61-
duration: step?.duration ?? null,
62-
}))
63-
: [],
56+
steps: (data.steps ?? []).map((step) => ({
57+
id: step?.id ?? '',
58+
timestamp: step?.timestamp ?? '',
59+
status: step?.status ?? 'PENDING',
60+
action: step?.action ?? null,
61+
duration: step?.duration ?? null,
62+
})),
6463
},
6564
}
6665
},

apps/sim/tools/tinyfish/list_runs.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import {
22
RUN_SUMMARY_OUTPUT_PROPERTIES,
33
type TinyFishListRunsParams,
44
type TinyFishListRunsResponse,
5+
type TinyFishRawRunList,
56
} from '@/tools/tinyfish/types'
67
import {
78
mapRunSummary,
@@ -94,12 +95,12 @@ export const listRunsTool: ToolConfig<TinyFishListRunsParams, TinyFishListRunsRe
9495
throw new Error(await tinyfishErrorMessage(response))
9596
}
9697

97-
const data = await response.json()
98+
const data = (await response.json()) as TinyFishRawRunList
9899

99100
return {
100101
success: true,
101102
output: {
102-
runs: Array.isArray(data.data) ? data.data.map(mapRunSummary) : [],
103+
runs: (data.data ?? []).map(mapRunSummary),
103104
total: data.pagination?.total ?? 0,
104105
nextCursor: data.pagination?.next_cursor ?? null,
105106
hasMore: data.pagination?.has_more ?? false,

apps/sim/tools/tinyfish/list_vault_items.ts

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import type {
22
TinyFishListVaultItemsParams,
33
TinyFishListVaultItemsResponse,
4+
TinyFishRawVaultItems,
45
} from '@/tools/tinyfish/types'
56
import {
67
TINYFISH_AGENT_API_BASE,
@@ -45,28 +46,24 @@ export const listVaultItemsTool: ToolConfig<
4546
throw new Error(await tinyfishErrorMessage(response))
4647
}
4748

48-
const data = await response.json()
49+
const data = (await response.json()) as TinyFishRawVaultItems
4950

5051
return {
5152
success: true,
5253
output: {
53-
items: Array.isArray(data.items)
54-
? data.items.map((item: any) => ({
55-
itemId: item?.itemId ?? '',
56-
connectionId: item?.connectionId ?? null,
57-
label: item?.label ?? '',
58-
vaultName: item?.vaultName ?? '',
59-
domains: item?.domains ?? [],
60-
fieldMetadata: Array.isArray(item?.fieldMetadata)
61-
? item.fieldMetadata.map((field: any) => ({
62-
fieldId: field?.fieldId ?? '',
63-
label: field?.label ?? '',
64-
type: field?.type ?? 'STRING',
65-
}))
66-
: [],
67-
hasTotp: item?.hasTotp ?? false,
68-
}))
69-
: [],
54+
items: (data.items ?? []).map((item) => ({
55+
itemId: item?.itemId ?? '',
56+
connectionId: item?.connectionId ?? null,
57+
label: item?.label ?? '',
58+
vaultName: item?.vaultName ?? '',
59+
domains: item?.domains ?? [],
60+
fieldMetadata: (item?.fieldMetadata ?? []).map((field) => ({
61+
fieldId: field?.fieldId ?? '',
62+
label: field?.label ?? '',
63+
type: field?.type ?? 'STRING',
64+
})),
65+
hasTotp: item?.hasTotp ?? false,
66+
})),
7067
},
7168
}
7269
},

apps/sim/tools/tinyfish/run.ts

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { tinyfishAgentHosting } from '@/tools/tinyfish/hosting'
22
import {
33
RUN_ERROR_OUTPUT_PROPERTIES,
44
SCHEMA_VALIDATION_OUTPUT_PROPERTIES,
5+
type TinyFishRawRun,
56
type TinyFishRunParams,
67
type TinyFishRunResponse,
78
} from '@/tools/tinyfish/types'
@@ -44,7 +45,7 @@ export const runTool: ToolConfig<TinyFishRunParams, TinyFishRunResponse> = {
4445
throw new Error(await tinyfishErrorMessage(response))
4546
}
4647

47-
const data = await response.json()
48+
const data = (await response.json()) as TinyFishRawRun
4849

4950
return {
5051
/**
@@ -54,15 +55,15 @@ export const runTool: ToolConfig<TinyFishRunParams, TinyFishRunResponse> = {
5455
success: data.status === 'COMPLETED',
5556
output: {
5657
runId: data.run_id ?? null,
57-
status: data.status,
58+
status: data.status ?? 'FAILED',
5859
startedAt: data.started_at ?? null,
5960
finishedAt: data.finished_at ?? null,
6061
numOfSteps: data.num_of_steps ?? null,
6162
result: data.result ?? null,
6263
schemaValidation: mapSchemaValidation(data.schema_validation),
6364
error: mapRunError(data.error),
6465
},
65-
error: data.error?.message,
66+
error: data.error?.message ?? undefined,
6667
}
6768
},
6869

0 commit comments

Comments
 (0)