11// Upload file to server using /files API
22
33import { readEnv } from '../internal/utils/env' ;
4- import fs from 'fs' ;
4+ import fs from 'fs/promises' ;
5+ import { createReadStream } from 'fs' ;
56import * as path from 'path' ;
6- import { FilePurpose } from '../resources' ;
7+ import { FilePurpose , FileRetrieveResponse } from '../resources' ;
78import { checkFile } from './check-file' ;
8-
9- export interface FileResponse {
10- id : string ;
11- object : string ;
12- type : 'jsonl' | 'parquet' ;
13- purpose : 'fine-tune' ;
14- filename : string ;
15- bytes : number ;
16- line_count : number ;
17- processed : boolean ;
18- }
9+ import { Together } from '../client' ;
10+ import { APIPromise } from '../core/api-promise' ;
1911
2012export interface ErrorResponse {
2113 message : string ;
@@ -27,114 +19,105 @@ const failedUploadMessage = {
2719
2820const baseURL = readEnv ( 'TOGETHER_API_BASE_URL' ) || 'https://api.together.xyz/v1' ;
2921
30- export async function upload (
22+ export function upload (
23+ client : Together ,
3124 fileName : string ,
3225 purpose : FilePurpose ,
3326 check : boolean = true ,
34- ) : Promise < FileResponse | ErrorResponse > {
35- if ( ! fs . existsSync ( fileName ) ) {
36- return {
37- message : 'File does not exists' ,
38- } ;
39- }
40-
41- const fileType = path . extname ( fileName ) ;
42- if ( fileType !== '.jsonl' && fileType !== '.parquet' ) {
43- return {
44- message : 'File type must be either .jsonl or .parquet' ,
45- } ;
46- }
47-
48- if ( check ) {
49- const checkResponse = await checkFile ( fileName , purpose ) ;
50- if ( ! checkResponse . is_check_passed ) {
51- return {
52- message : checkResponse . message || `verification of ${ fileName } failed with some unknown reason` ,
53- } ;
54- }
55- }
56-
57- // steps to do
58- // 1. check if file exists
59- // 2. get signed upload url
60- // 3. upload file
61- const apiKey = readEnv ( 'TOGETHER_API_KEY' ) ;
62-
63- if ( ! apiKey ) {
64- return {
65- message : 'API key is required' ,
66- } ;
67- }
68-
69- const getSigned = baseURL + '/files' ;
70-
71- try {
72- const params = new URLSearchParams ( {
73- file_name : fileName ,
74- purpose : purpose ,
75- } ) ;
76- const fullUrl = `${ getSigned } ?${ params } ` ;
77- const r = await fetch ( fullUrl , {
78- method : 'POST' ,
79- headers : {
80- 'Content-Type' : 'application/x-www-form-urlencoded' ,
81- Authorization : `Bearer ${ apiKey } ` ,
82- } ,
83- redirect : 'manual' ,
84- body : params . toString ( ) ,
85- } ) ;
86-
87- if ( r . status !== 302 ) {
88- return failedUploadMessage ;
89- }
90-
91- const uploadUrl = r . headers . get ( 'location' ) || '' ;
92- if ( ! uploadUrl || uploadUrl === '' ) {
93- return failedUploadMessage ;
94- }
95- const fileId = r . headers . get ( 'x-together-file-id' ) || '' ;
96- if ( ! fileId || fileId === '' ) {
97- return failedUploadMessage ;
98- }
99-
100- const fileStream = fs . createReadStream ( fileName ) ;
101- const fileSize = fs . statSync ( fileName ) . size ;
102-
103- // upload the file to uploadUrl
104- const uploadResponse = await fetch ( uploadUrl , {
105- method : 'PUT' ,
106- headers : {
107- 'Content-Type' : 'application/octet-stream' ,
108- 'Content-Length' : `${ fileSize } ` ,
109- } ,
110- body : fileStream ,
111- } ) ;
112-
113- if ( uploadResponse . status !== 200 ) {
114- return {
115- message : `failed to upload file (${ uploadResponse . statusText } ) status code ${ uploadResponse . status } ` ,
116- } ;
117- }
118-
119- return {
120- id : fileId ,
121- object : 'file' ,
122- type : 'jsonl' ,
123- purpose : 'fine-tune' ,
124- filename : fileName ,
125- bytes : fileSize ,
126- line_count : 0 ,
127- processed : true ,
128- } ;
129- } catch ( error ) {
130- if ( error instanceof Error && 'status' in error && error . status ) {
131- return {
132- message : `failed to upload file with status ${ error . status } ` ,
133- } ;
134- }
135-
136- return {
137- message : 'failed to upload file' ,
138- } ;
139- }
27+ ) : APIPromise < FileRetrieveResponse > {
28+ return new APIPromise < FileRetrieveResponse > (
29+ client ,
30+ new Promise ( async ( resolve , reject ) => {
31+ let fileSize = 0 ;
32+
33+ try {
34+ const stat = await fs . stat ( fileName ) ;
35+ fileSize = stat . size ;
36+ } catch {
37+ reject ( new Error ( 'File does not exists' ) ) ;
38+ }
39+
40+ const fileType = path . extname ( fileName ) . replace ( '.' , '' ) ;
41+ if ( fileType !== 'jsonl' && fileType !== 'parquet' && fileType !== 'csv' ) {
42+ return {
43+ message : 'File type must be either .jsonl, .parquet, or .csv' ,
44+ } ;
45+ }
46+
47+ if ( check ) {
48+ const checkResponse = await checkFile ( fileName , purpose ) ;
49+ if ( ! checkResponse . is_check_passed ) {
50+ reject ( checkResponse . message || `verification of ${ fileName } failed with some unknown reason` ) ;
51+ }
52+ }
53+
54+ try {
55+ const params = new URLSearchParams ( {
56+ file_name : fileName ,
57+ file_type : fileType ,
58+ purpose : purpose ,
59+ } ) ;
60+ const fullUrl = `${ baseURL } /files?${ params } ` ;
61+ const r = await fetch ( fullUrl , {
62+ method : 'POST' ,
63+ headers : {
64+ 'Content-Type' : 'application/x-www-form-urlencoded' ,
65+ Authorization : `Bearer ${ client . apiKey } ` ,
66+ } ,
67+ redirect : 'manual' ,
68+ body : params . toString ( ) ,
69+ } ) ;
70+
71+ if ( r . status !== 302 ) {
72+ return reject ( failedUploadMessage ) ;
73+ }
74+
75+ const uploadUrl = r . headers . get ( 'location' ) || '' ;
76+ if ( ! uploadUrl || uploadUrl === '' ) {
77+ return reject ( failedUploadMessage ) ;
78+ }
79+ const fileId = r . headers . get ( 'x-together-file-id' ) || '' ;
80+ if ( ! fileId || fileId === '' ) {
81+ return reject ( failedUploadMessage ) ;
82+ }
83+
84+ const fileStream = createReadStream ( fileName ) ;
85+
86+ // upload the file to uploadUrl
87+ const uploadResponse = await fetch ( uploadUrl , {
88+ method : 'PUT' ,
89+ headers : {
90+ 'Content-Type' : 'application/octet-stream' ,
91+ 'Content-Length' : fileSize . toString ( ) ,
92+ } ,
93+ body : fileStream ,
94+ } ) ;
95+
96+ // uploadResponse.
97+
98+ if ( uploadResponse . status !== 200 ) {
99+ return reject ( {
100+ message : `failed to upload file (${ uploadResponse . statusText } ) status code ${ uploadResponse . status } ` ,
101+ } ) ;
102+ }
103+
104+ const data = await client . files . retrieve ( fileId ) . asResponse ( ) ;
105+
106+ // Forcing the shape into the APIResponse interface
107+ resolve ( {
108+ controller : new AbortController ( ) ,
109+ requestLogID : '' ,
110+ retryOfRequestLogID : undefined ,
111+ startTime : Date . now ( ) ,
112+ options : {
113+ method : 'post' ,
114+ path : '/files' ,
115+ } ,
116+ response : data ,
117+ } ) ;
118+ } catch ( error ) {
119+ reject ( error ) ;
120+ }
121+ } ) ,
122+ ) ;
140123}
0 commit comments