Skip to content

Commit b37545f

Browse files
committed
[api] update get history to take an array
1 parent 834ac3e commit b37545f

File tree

3 files changed

+162
-6
lines changed

3 files changed

+162
-6
lines changed

src/schemas/apiSchema.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,20 @@ const zHistoryTaskItem = z.object({
235235
meta: zTaskMeta.optional()
236236
})
237237

238+
// Raw history item from backend (without taskType)
239+
const zRawHistoryItem = z.object({
240+
prompt_id: zPromptId,
241+
prompt: zTaskPrompt,
242+
status: zStatus.optional(),
243+
outputs: zTaskOutput,
244+
meta: zTaskMeta.optional()
245+
})
246+
247+
// New API response format: { history: [{prompt_id: "...", ...}, ...] }
248+
const zHistoryResponse = z.object({
249+
history: z.array(zRawHistoryItem)
250+
})
251+
238252
const zTaskItem = z.union([
239253
zRunningTaskItem,
240254
zPendingTaskItem,
@@ -257,6 +271,8 @@ export type RunningTaskItem = z.infer<typeof zRunningTaskItem>
257271
export type PendingTaskItem = z.infer<typeof zPendingTaskItem>
258272
// `/history`
259273
export type HistoryTaskItem = z.infer<typeof zHistoryTaskItem>
274+
export type RawHistoryItem = z.infer<typeof zRawHistoryItem>
275+
export type HistoryResponse = z.infer<typeof zHistoryResponse>
260276
export type TaskItem = z.infer<typeof zTaskItem>
261277

262278
export function validateTaskItem(taskItem: unknown) {

src/scripts/api.ts

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type {
1111
ExecutionStartWsMessage,
1212
ExecutionSuccessWsMessage,
1313
ExtensionsResponse,
14+
HistoryResponse,
1415
HistoryTaskItem,
1516
LogsRawResponse,
1617
LogsWsMessage,
@@ -710,13 +711,17 @@ export class ComfyApi extends EventTarget {
710711
max_items: number = 200
711712
): Promise<{ History: HistoryTaskItem[] }> {
712713
try {
713-
const res = await this.fetchApi(`/history?max_items=${max_items}`)
714-
const json: Promise<HistoryTaskItem[]> = await res.json()
714+
const res = await this.fetchApi(`/history_v2?max_items=${max_items}`)
715+
const json: HistoryResponse = await res.json()
716+
717+
// Extract history data from new format: { history: [{prompt_id: "...", ...}, ...] }
715718
return {
716-
History: Object.values(json).map((item) => ({
717-
...item,
718-
taskType: 'History'
719-
}))
719+
History: json.history.map(
720+
(item): HistoryTaskItem => ({
721+
...item,
722+
taskType: 'History'
723+
})
724+
)
720725
}
721726
} catch (error) {
722727
console.error(error)

tests-ui/tests/scripts/api.test.ts

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
import { beforeEach, describe, expect, it, vi } from 'vitest'
2+
3+
import type {
4+
HistoryResponse,
5+
RawHistoryItem
6+
} from '../../../src/schemas/apiSchema'
7+
import { ComfyApi } from '../../../src/scripts/api'
8+
9+
describe('ComfyApi getHistory', () => {
10+
let api: ComfyApi
11+
12+
beforeEach(() => {
13+
api = new ComfyApi()
14+
})
15+
16+
const mockHistoryItem: RawHistoryItem = {
17+
prompt_id: 'test_prompt_id',
18+
prompt: [
19+
0,
20+
'test_prompt_id',
21+
{
22+
'1': {
23+
class_type: 'CheckpointLoaderSimple',
24+
inputs: {
25+
ckpt_name: 'model.safetensors'
26+
}
27+
}
28+
},
29+
{
30+
extra_pnginfo: {
31+
workflow: {
32+
last_node_id: 1,
33+
last_link_id: 0,
34+
nodes: [],
35+
links: [],
36+
groups: [],
37+
config: {},
38+
extra: {},
39+
version: 0.4
40+
}
41+
},
42+
client_id: 'test_client_id'
43+
},
44+
['1']
45+
],
46+
outputs: {},
47+
status: {
48+
status_str: 'success',
49+
completed: true,
50+
messages: []
51+
}
52+
}
53+
54+
describe('history v2 API format', () => {
55+
it('should handle history array format from /history_v2', async () => {
56+
const historyResponse: HistoryResponse = {
57+
history: [
58+
{ ...mockHistoryItem, prompt_id: 'prompt_id_1' },
59+
{ ...mockHistoryItem, prompt_id: 'prompt_id_2' }
60+
]
61+
}
62+
63+
// Mock fetchApi to return the v2 format
64+
const mockFetchApi = vi.fn().mockResolvedValue({
65+
json: vi.fn().mockResolvedValue(historyResponse)
66+
})
67+
api.fetchApi = mockFetchApi
68+
69+
const result = await api.getHistory(10)
70+
71+
expect(result.History).toHaveLength(2)
72+
expect(result.History[0]).toEqual({
73+
...mockHistoryItem,
74+
prompt_id: 'prompt_id_1',
75+
taskType: 'History'
76+
})
77+
expect(result.History[1]).toEqual({
78+
...mockHistoryItem,
79+
prompt_id: 'prompt_id_2',
80+
taskType: 'History'
81+
})
82+
})
83+
84+
it('should handle empty history array', async () => {
85+
const historyResponse: HistoryResponse = {
86+
history: []
87+
}
88+
89+
const mockFetchApi = vi.fn().mockResolvedValue({
90+
json: vi.fn().mockResolvedValue(historyResponse)
91+
})
92+
api.fetchApi = mockFetchApi
93+
94+
const result = await api.getHistory(10)
95+
96+
expect(result.History).toHaveLength(0)
97+
expect(result.History).toEqual([])
98+
})
99+
})
100+
101+
describe('error handling', () => {
102+
it('should return empty history on error', async () => {
103+
const mockFetchApi = vi.fn().mockRejectedValue(new Error('Network error'))
104+
api.fetchApi = mockFetchApi
105+
106+
const result = await api.getHistory()
107+
108+
expect(result.History).toEqual([])
109+
})
110+
})
111+
112+
describe('API call parameters', () => {
113+
it('should call fetchApi with correct v2 endpoint and parameters', async () => {
114+
const mockFetchApi = vi.fn().mockResolvedValue({
115+
json: vi.fn().mockResolvedValue({ history: [] })
116+
})
117+
api.fetchApi = mockFetchApi
118+
119+
await api.getHistory(50)
120+
121+
expect(mockFetchApi).toHaveBeenCalledWith('/history_v2?max_items=50')
122+
})
123+
124+
it('should use default max_items parameter with v2 endpoint', async () => {
125+
const mockFetchApi = vi.fn().mockResolvedValue({
126+
json: vi.fn().mockResolvedValue({ history: [] })
127+
})
128+
api.fetchApi = mockFetchApi
129+
130+
await api.getHistory()
131+
132+
expect(mockFetchApi).toHaveBeenCalledWith('/history_v2?max_items=200')
133+
})
134+
})
135+
})

0 commit comments

Comments
 (0)