File size: 3,111 Bytes
38ceaf9
 
 
 
 
 
 
 
1db49be
38ceaf9
 
 
1db49be
38ceaf9
 
 
916c5da
 
 
 
1db49be
916c5da
38ceaf9
 
 
 
 
1db49be
38ceaf9
1db49be
38ceaf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1db49be
38ceaf9
1db49be
 
 
 
 
 
38ceaf9
1db49be
38ceaf9
 
 
 
 
 
 
 
 
 
 
1db49be
38ceaf9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
5d7708a
38ceaf9
 
 
 
 
 
 
 
 
 
 
 
 
916c5da
 
 
 
 
 
 
 
1db49be
916c5da
 
 
 
 
1db49be
916c5da
 
 
 
 
 
 
 
1db49be
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
import { z } from "zod";

const runTypes = z.object({
  run_id: z.string(),
});

const runOutputTypes = z.object({
  id: z.string(),
  status: z.enum(["success", "failed", "running", "uploading", "not-started"]),
  outputs: z.array(
    z.object({
      data: z.any(),
    }),
  ),
});

const uploadFileTypes = z.object({
  upload_url: z.string(),
  file_id: z.string(),
  download_url: z.string(),
});

export class ComfyDeployClient {
  apiBase: string = "https://www.comfydeploy.com/api";
  apiToken: string;

  constructor({ apiBase, apiToken }: { apiBase?: string; apiToken: string }) {
    if (apiBase) {
      this.apiBase = `${apiBase}/api`;
    }
    this.apiToken = apiToken;
  }

  async run({
    deployment_id,
    inputs,
  }: {
    deployment_id: string;
    inputs?: Record<string, string>;
  }) {
    return fetch(`${this.apiBase}/run`, {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
        authorization: `Bearer ${this.apiToken}`,
      },
      body: JSON.stringify({
        deployment_id: deployment_id,
        inputs: inputs,
      }),
      cache: "no-store",
    })
      .then((response) => {
        console.log('response', response)
        return response.json()})
      .then((json) => {
        console.log('json', json)
        return runTypes.parse(json)})
      .catch((err) => {
        console.error('err', err);
        return null;
      });
  }

  async getRun(run_id: string) {
    return await fetch(`${this.apiBase}/run?run_id=${run_id}`, {
      method: "GET",
      headers: {
        "Content-Type": "application/json",
        authorization: `Bearer ${this.apiToken}`,
      },
      cache: "no-store",
    })
      .then((response) => response.json())
      .then((json) => runOutputTypes.parse(json))
      .catch((err) => {
        console.error(err);
        return null;
      });
  }

  async runSync(props: {
    deployment_id: string;
    inputs?: Record<string, string>;
  }) {
    const runResult = await this.run(props);
    if (!runResult) return null;

    // 5 minutes
    const timeout = 60 * 5;
    const interval = 1000;

    let run: Awaited<ReturnType<typeof this.getRun>> = null;
    for (let i = 0; i < timeout; i++) {
      run = await this.getRun(runResult.run_id);
      if (run && run.status == "success") {
        break;
      }
      await new Promise((resolve) => setTimeout(resolve, interval));
    }

    if (!run) {
      return {
        id: runResult.run_id,
      };
    }

    return run;
  }

  async getUploadUrl(type: string, file_size: number) {
    const obj = {
      type: type,
      file_size: file_size.toString(),
    };
    const url = new URL(`${this.apiBase}/upload-url`);
    url.search = new URLSearchParams(obj).toString();

    return await fetch(url.href, {
      method: "GET",
      headers: {
        authorization: `Bearer ${this.apiToken}`,
      },
      cache: "no-store",
    })
      .then((response) => response.json())
      .then((json) => uploadFileTypes.parse(json))
      .catch((err) => {
        console.error(err);
        return null;
      });
  }
}