Files
mistral-go-sdk/conversations.go
vikingowl 2b980e14b3 fix: post-review fixes — metadata, unknown types, typed tools, API polish
1. Add README, LICENSE (MIT), .gitignore, Makefile, CHANGELOG
2. Add Version constant and User-Agent header to all requests
3. Rename SetStream to EnableStream (narrower API surface)
4. Fix FinishReason in CompletionStreamChoice to use typed *FinishReason
5. Type conversation entry Content as chat.Content instead of json.RawMessage
6. Graceful unknown type handling — UnknownEntry, UnknownEvent,
   UnknownChunk, UnknownMessage, UnknownAgentTool all return data
   instead of erroring on unrecognized discriminator values
7. Type agent tools with AgentTool sealed interface + UnmarshalAgentTool
8. Add pagination params to ListConversations and ListLibraries
9. Move openapi.yaml to docs/openapi.yaml
2026-03-05 20:51:24 +01:00

176 lines
5.6 KiB
Go

package mistral
import (
"context"
"encoding/json"
"fmt"
"net/url"
"strconv"
"somegit.dev/vikingowl/mistral-go-sdk/conversation"
)
// StartConversation creates and starts a new conversation.
func (c *Client) StartConversation(ctx context.Context, req *conversation.StartRequest) (*conversation.Response, error) {
var resp conversation.Response
if err := c.doJSON(ctx, "POST", "/v1/conversations", req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// StartConversationStream creates a conversation and returns a stream of events.
func (c *Client) StartConversationStream(ctx context.Context, req *conversation.StartRequest) (*EventStream, error) {
req.EnableStream()
resp, err := c.doStream(ctx, "POST", "/v1/conversations", req)
if err != nil {
return nil, err
}
return newEventStream(resp.Body), nil
}
// AppendConversation appends inputs to an existing conversation.
func (c *Client) AppendConversation(ctx context.Context, conversationID string, req *conversation.AppendRequest) (*conversation.Response, error) {
var resp conversation.Response
if err := c.doJSON(ctx, "POST", fmt.Sprintf("/v1/conversations/%s", conversationID), req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// AppendConversationStream appends to a conversation and returns a stream of events.
func (c *Client) AppendConversationStream(ctx context.Context, conversationID string, req *conversation.AppendRequest) (*EventStream, error) {
req.EnableStream()
resp, err := c.doStream(ctx, "POST", fmt.Sprintf("/v1/conversations/%s", conversationID), req)
if err != nil {
return nil, err
}
return newEventStream(resp.Body), nil
}
// RestartConversation restarts a conversation from a specific entry.
func (c *Client) RestartConversation(ctx context.Context, conversationID string, req *conversation.RestartRequest) (*conversation.Response, error) {
var resp conversation.Response
if err := c.doJSON(ctx, "POST", fmt.Sprintf("/v1/conversations/%s/restart", conversationID), req, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// RestartConversationStream restarts a conversation and returns a stream of events.
func (c *Client) RestartConversationStream(ctx context.Context, conversationID string, req *conversation.RestartRequest) (*EventStream, error) {
req.EnableStream()
resp, err := c.doStream(ctx, "POST", fmt.Sprintf("/v1/conversations/%s/restart", conversationID), req)
if err != nil {
return nil, err
}
return newEventStream(resp.Body), nil
}
// GetConversation retrieves conversation metadata.
func (c *Client) GetConversation(ctx context.Context, conversationID string) (*conversation.Conversation, error) {
var resp conversation.Conversation
if err := c.doJSON(ctx, "GET", fmt.Sprintf("/v1/conversations/%s", conversationID), nil, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// ListConversations lists conversations with optional pagination.
func (c *Client) ListConversations(ctx context.Context, params *conversation.ListParams) ([]conversation.Conversation, error) {
path := "/v1/conversations"
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 encoded := q.Encode(); encoded != "" {
path += "?" + encoded
}
}
var resp []conversation.Conversation
if err := c.doJSON(ctx, "GET", path, nil, &resp); err != nil {
return nil, err
}
return resp, nil
}
// DeleteConversation deletes a conversation.
func (c *Client) DeleteConversation(ctx context.Context, conversationID string) error {
resp, err := c.do(ctx, "DELETE", fmt.Sprintf("/v1/conversations/%s", conversationID), nil)
if err != nil {
return err
}
defer resp.Body.Close()
if resp.StatusCode >= 400 {
return parseAPIError(resp)
}
return nil
}
// GetConversationHistory returns the full history of a conversation.
func (c *Client) GetConversationHistory(ctx context.Context, conversationID string) (*conversation.History, error) {
var resp conversation.History
if err := c.doJSON(ctx, "GET", fmt.Sprintf("/v1/conversations/%s/history", conversationID), nil, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// GetConversationMessages returns the messages of a conversation.
func (c *Client) GetConversationMessages(ctx context.Context, conversationID string) (*conversation.Messages, error) {
var resp conversation.Messages
if err := c.doJSON(ctx, "GET", fmt.Sprintf("/v1/conversations/%s/messages", conversationID), nil, &resp); err != nil {
return nil, err
}
return &resp, nil
}
// EventStream wraps the generic Stream to provide typed conversation events.
type EventStream struct {
stream *Stream[json.RawMessage]
event conversation.Event
err error
}
func newEventStream(body readCloser) *EventStream {
return &EventStream{
stream: newStream[json.RawMessage](body),
}
}
// Next advances to the next event. Returns false when done or on error.
func (s *EventStream) Next() bool {
if s.err != nil {
return false
}
if !s.stream.Next() {
s.err = s.stream.Err()
return false
}
event, err := conversation.UnmarshalEvent(s.stream.Current())
if err != nil {
s.err = err
return false
}
s.event = event
return true
}
// Current returns the most recently read event.
func (s *EventStream) Current() conversation.Event { return s.event }
// Err returns any error encountered during streaming.
func (s *EventStream) Err() error { return s.err }
// Close releases the underlying connection.
func (s *EventStream) Close() error { return s.stream.Close() }
type readCloser = interface {
Read(p []byte) (n int, err error)
Close() error
}