Add model and file management: - model/model.go: ModelCard (unified base/fine-tuned), ModelCapabilities - file/file.go: File, ListParams, Purpose/SampleType/Source enums - ListModels, GetModel, DeleteModel service methods - UploadFile (multipart/form-data), ListFiles (query params), GetFile, DeleteFile, GetFileContent (binary stream), GetFileURL (signed URL) - doMultipart() HTTP helper for file uploads - 13 new tests covering all endpoints including multipart parsing
106 lines
2.9 KiB
Go
106 lines
2.9 KiB
Go
package mistral
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/url"
|
|
"strconv"
|
|
|
|
"somegit.dev/vikingowl/mistral-go-sdk/file"
|
|
)
|
|
|
|
// UploadFile uploads a file for use with fine-tuning, batch, or OCR.
|
|
func (c *Client) UploadFile(ctx context.Context, filename string, r io.Reader, purpose file.Purpose) (*file.File, error) {
|
|
fields := map[string]string{}
|
|
if purpose != "" {
|
|
fields["purpose"] = string(purpose)
|
|
}
|
|
var resp file.File
|
|
if err := c.doMultipart(ctx, "/v1/files", filename, r, fields, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
// ListFiles returns a list of uploaded files.
|
|
func (c *Client) ListFiles(ctx context.Context, params *file.ListParams) (*file.ListResponse, error) {
|
|
path := "/v1/files"
|
|
if params != nil {
|
|
q := url.Values{}
|
|
if params.Page != nil {
|
|
q.Set("page", strconv.Itoa(*params.Page))
|
|
}
|
|
if params.PageSize != nil {
|
|
q.Set("page_size", strconv.Itoa(*params.PageSize))
|
|
}
|
|
if params.Purpose != nil {
|
|
q.Set("purpose", string(*params.Purpose))
|
|
}
|
|
if params.Search != nil {
|
|
q.Set("search", *params.Search)
|
|
}
|
|
if encoded := q.Encode(); encoded != "" {
|
|
path += "?" + encoded
|
|
}
|
|
}
|
|
var resp file.ListResponse
|
|
if err := c.doJSON(ctx, "GET", path, nil, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
// GetFile retrieves file metadata by ID.
|
|
func (c *Client) GetFile(ctx context.Context, fileID string) (*file.File, error) {
|
|
var resp file.File
|
|
if err := c.doJSON(ctx, "GET", "/v1/files/"+fileID, nil, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
// DeleteFile deletes a file by ID.
|
|
func (c *Client) DeleteFile(ctx context.Context, fileID string) (*file.DeleteResponse, error) {
|
|
var resp file.DeleteResponse
|
|
if err := c.doJSON(ctx, "DELETE", "/v1/files/"+fileID, nil, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
// GetFileContent downloads the raw content of a file.
|
|
// The caller must close the returned ReadCloser.
|
|
func (c *Client) GetFileContent(ctx context.Context, fileID string) (io.ReadCloser, error) {
|
|
resp, err := c.do(ctx, "GET", "/v1/files/"+fileID+"/content", nil)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
if resp.StatusCode >= 400 {
|
|
defer resp.Body.Close()
|
|
return nil, parseAPIError(resp)
|
|
}
|
|
return resp.Body, nil
|
|
}
|
|
|
|
// GetFileURL returns a signed URL for downloading a file.
|
|
// Expiry is in hours (default 24 if 0).
|
|
func (c *Client) GetFileURL(ctx context.Context, fileID string, expiryHours int) (*file.SignedURL, error) {
|
|
path := fmt.Sprintf("/v1/files/%s/url", fileID)
|
|
if expiryHours > 0 {
|
|
path += fmt.Sprintf("?expiry=%d", expiryHours)
|
|
}
|
|
var resp file.SignedURL
|
|
if err := c.doJSON(ctx, "GET", path, nil, &resp); err != nil {
|
|
return nil, err
|
|
}
|
|
return &resp, nil
|
|
}
|
|
|
|
// doRawGet performs a GET request and returns the raw response.
|
|
// Used for endpoints that return non-JSON data.
|
|
func (c *Client) doRawGet(ctx context.Context, path string) (*http.Response, error) {
|
|
return c.do(ctx, "GET", path, nil)
|
|
}
|