FILE / ScuroNeko/go-deepseek

completion.go

Исходный файл и его история в репозитории.
FILE 1bb05f093837f713eac4ad5f8addc08afad20755
Files
go-deepseek/completion.go
T
2026-07-31 15:21:56 +03:00

160 lines
4.2 KiB
Go

package deepseek
import (
"context"
"errors"
"io"
"net/http"
"strings"
)
// CompletionReq configures a chat completion request.
type CompletionReq struct {
ChatSessionID string `json:"chat_session_id"`
ParentMessageID *uint64 `json:"parent_message_id"`
ModelType ModelType `json:"model_type"`
promptBuilder strings.Builder
Prompt string `json:"prompt"`
RefFileIDs []string `json:"ref_file_ids"`
ThinkingEnabled bool `json:"thinking_enabled"`
SearchEnabled bool `json:"search_enabled"`
}
// NewCompletionReq creates a completion request for chatID using the default
// model type.
func NewCompletionReq(chatID string) *CompletionReq {
return &CompletionReq{
ChatSessionID: chatID, ModelType: ModelTypeDefault,
}
}
// SetParentMessageID sets the parent message for a continued conversation.
func (req *CompletionReq) SetParentMessageID(id uint64) *CompletionReq {
req.ParentMessageID = new(id)
return req
}
// SetModelType selects the model used for the completion.
func (req *CompletionReq) SetModelType(t ModelType) *CompletionReq {
req.ModelType = t
return req
}
// AddPrompt appends s to the prompt under construction.
func (req *CompletionReq) AddPrompt(s string) *CompletionReq {
_, _ = req.promptBuilder.WriteString(s)
return req
}
// BuildPrompt stores the accumulated prompt and resets the internal builder.
func (req *CompletionReq) BuildPrompt() *CompletionReq {
req.Prompt = req.promptBuilder.String()
req.promptBuilder = strings.Builder{}
return req
}
// SetPrompt replaces the request prompt.
func (req *CompletionReq) SetPrompt(prompt string) *CompletionReq {
req.Prompt = prompt
return req
}
// SetThinkingEnabled enables or disables model thinking output.
func (req *CompletionReq) SetThinkingEnabled(b bool) *CompletionReq {
req.ThinkingEnabled = b
return req
}
// SetSearchEnabled enables or disables web search for the completion.
func (req *CompletionReq) SetSearchEnabled(b bool) *CompletionReq {
req.SearchEnabled = b
return req
}
// ModelType identifies a DeepSeek chat model mode.
type ModelType string
const (
// ModelTypeDefault selects the default model mode.
ModelTypeDefault ModelType = "default"
// ModelTypeExpert selects the expert model mode.
ModelTypeExpert ModelType = "expert"
)
// Completion starts a streaming completion using a background context.
// The caller owns and must either close the response body or pass the response
// to ReadStream or ReadStreamAsString.
func (api *Client) Completion(body CompletionReq) (*http.Response, error) {
return api.CompletionWithContext(context.Background(), body)
}
// CompletionWithContext starts a streaming completion and honors ctx
// cancellation. The caller owns and must either close the response body or
// pass the response to ReadStream or ReadStreamAsString.
func (api *Client) CompletionWithContext(ctx context.Context, body CompletionReq) (*http.Response, error) {
req := NewRequest[CreatePowChallengeRes]("POST", "chat/completion", body)
err := req.SolvePow(ctx, api)
if err != nil {
return nil, err
}
resp, err := req.DoWithContext(ctx, api)
if err != nil {
return nil, err
}
return resp, nil
}
// ReadStream consumes and closes resp.Body, updates stream state, and calls
// onText for newly appended response text. A nil callback is allowed.
func ReadStream(ctx context.Context, resp *http.Response, onText func(string)) (*StreamState, error) {
defer resp.Body.Close()
reader := NewSSEReader(resp.Body)
state := &StreamState{}
var previousLength int
for {
event, err := reader.Next(ctx)
if errors.Is(err, io.EOF) {
break
}
if err != nil {
return nil, err
}
if err := state.Apply(event); err != nil {
return nil, err
}
current := state.Content.String()
if len(current) > previousLength {
chunk := current[previousLength:]
if onText != nil {
onText(chunk)
}
previousLength = len(current)
}
if event.Event == "close" {
break
}
}
return state, nil
}
// ReadStreamAsString consumes and closes resp.Body and returns the final
// response content.
func ReadStreamAsString(ctx context.Context, resp *http.Response) (string, error) {
state, err := ReadStream(ctx, resp, nil)
if err != nil {
return "", err
}
return state.Content.String(), nil
}