deepseek

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 31, 2025 License: MIT Imports: 12 Imported by: 36

README

Deepseek-Go

MIT License Go Report Card

Deepseek-Go is a Go-based API wrapper for the Deepseek platform. It provides a clean and type-safe interface to interact with Deepseek's AI features, including chat completions with streaming, token usage tracking, and more.

This library is designed for developers building Go applications that require seamless integration with Deepseek AI.

The recent gain in popularity and cybersecurity issues Deepseek has seen makes for many problems while using the API. Please refer to the status page for the current status.

Features

  • Chat Completion: Easily send chat messages and receive responses from Deepseek's AI models. It also supports streaming.
  • Modular Design: The library is structured into reusable components for building, sending, and handling requests and responses.
  • MIT License: Open-source and free for both personal and commercial use.

Installation

To use Deepseek-Go, ensure you have Go installed, and run:

go get github.com/cohesion-org/deepseek-go

Getting Started

Here's a quick example of how to use the library:

Prerequisites

Before using the library, ensure you have:

  • A valid Deepseek API key.
  • Go installed on your system.
Supported Models
  • deepseek-chat
    A versatile model designed for conversational tasks.
    Usage: Model: deepseek.DeepSeekChat

  • deepseek-reasoner
    A specialized model for reasoning-based tasks.
    Usage: Model: deepseek.DeepSeekReasoner.
    Note: The reasoner requires unique conditions. Please refer to this issue #8.

Chat
Example for chatting with deepseek
package main

import (
	"context"
	"fmt"
	"log"
	"os"

	deepseek "github.com/cohesion-org/deepseek-go"
	constants "github.com/cohesion-org/deepseek-go/constants"
)

func main() {
	// Set up the Deepseek client
    client := deepseek.NewClient(os.Getenv("DEEPSEEK_API_KEY"))

	// Create a chat completion request
	request := &deepseek.ChatCompletionRequest{
		Model: deepseek.DeepSeekChat,
		Messages: []deepseek.ChatCompletionMessage{
			{Role: constants.ChatMessageRoleSystem, Content: "Answer every question using slang."},
			{Role: constants.ChatMessageRoleUser, Content: "Which is the tallest mountain in the world?"},
		},
	}

	// Send the request and handle the response
	ctx := context.Background()
	response, err := client.CreateChatCompletion(ctx, request)
	if err != nil {
		log.Fatalf("error: %v", err)
	}

	// Print the response
	fmt.Println("Response:", response.Choices[0].Message.Content)
}

Save this code to a file (e.g., main.go), and run it:

go run main.go
Sending other params like Temp, Stop You just need to extend the ChatCompletionMessage with the supported parameters.
	request := &deepseek.ChatCompletionRequest{
		Model: deepseek.DeepSeekChat,
		Messages: []deepseek.ChatCompletionMessage{
			{Role: constants.ChatMessageRoleUser, Content: "What is the meaning of deepseek"},
			{Role: constants.ChatMessageRoleSystem, Content: "Answer every question using slang"},
		},
		Temperature: 1.0,
		Stop:        []string{"yo", "hello"},
		ResponseFormat: &deepseek.ResponseFormat{
			Type: "text",
		},
	}
Multi-Conversation with Deepseek.
package deepseek_examples

import (
	"context"
	"log"

	deepseek "github.com/cohesion-org/deepseek-go"
	"github.com/cohesion-org/deepseek-go/constants"
)

func MultiChat() {
	client := deepseek.NewClient("DEEPSEEK_API_KEY")
	ctx := context.Background()

	messages := []deepseek.ChatCompletionMessage{{
		Role:    constants.ChatMessageRoleUser,
		Content: "Who is the president of the United States? One word response only.",
	}}

	// Round 1: First API call
	response1, err := client.CreateChatCompletion(ctx, &deepseek.ChatCompletionRequest{
		Model:    deepseek.DeepSeekChat,
		Messages: messages,
	})
	if err != nil {
		log.Fatalf("Round 1 failed: %v", err)
	}

	response1Message, err := deepseek.MapMessageToChatCompletionMessage(response1.Choices[0].Message)
	if err != nil {
		log.Fatalf("Mapping to message failed: %v", err)
	}
	messages = append(messages, response1Message)

	log.Printf("The messages after response 1 are: %v", messages)
	// Round 2: Second API call
	messages = append(messages, deepseek.ChatCompletionMessage{
		Role:    constants.ChatMessageRoleUser,
		Content: "Who was the one in the previous term.",
	})

	response2, err := client.CreateChatCompletion(ctx, &deepseek.ChatCompletionRequest{
		Model:    deepseek.DeepSeekChat,
		Messages: messages,
	})
	if err != nil {
		log.Fatalf("Round 2 failed: %v", err)
	}

	response2Message, err := deepseek.MapMessageToChatCompletionMessage(response2.Choices[0].Message)
	if err != nil {
		log.Fatalf("Mapping to message failed: %v", err)
	}
	messages = append(messages, response2Message)
	log.Printf("The messages after response 1 are: %v", messages)

}

Chat with Streaming
package main

import (
	"context"
	"errors"
	"fmt"
	"io"
	"log"
	"os"

	deepseek "github.com/cohesion-org/deepseek-go"
	constants "github.com/cohesion-org/deepseek-go/constants"
)

func main() {
	client := deepseek.NewClient(os.Getenv("DEEPSEEK_API_KEY"))
	request := &deepseek.StreamChatCompletionRequest{
		Model: deepseek.DeepSeekChat,
		Messages: []deepseek.ChatCompletionMessage{
			{Role: constants.ChatMessageRoleUser, Content: "Just testing if the streaming feature is working or not!"},
		},
		Stream: true,
	}
	ctx := context.Background()

	stream, err := client.CreateChatCompletionStream(ctx, request)
	if err != nil {
		log.Fatalf("ChatCompletionStream error: %v", err)
	}
	var fullMessage string
	defer stream.Close()
	for {
		response, err := stream.Recv()
		if errors.Is(err, io.EOF) {
			fmt.Println("\nStream finished")
			break
		}
		if err != nil {
			fmt.Printf("\nStream error: %v\n", err)
			break
		}
		for _, choice := range response.Choices {
			fullMessage += choice.Delta.Content // Accumulate chunk content
			log.Println(choice.Delta.Content)
		}
	}
	log.Println("The full message is: ", fullMessage)
}
Get the balance(s) of the user.
package main

import (
	"context"
	"log"
	"os"

	deepseek "github.com/cohesion-org/deepseek-go"
)

func main() {
	client := deepseek.NewClient(os.Getenv("DEEPSEEK_API_KEY"))
	ctx := context.Background()
	balance, err := deepseek.GetBalance(client, ctx)
	if err != nil {
		log.Fatalf("Error getting balance: %v", err)
	}

	if balance == nil {
		log.Fatalf("Balance is nil")
	}

	if len(balance.BalanceInfos) == 0 {
		log.Fatalf("No balance information returned")
	}
	log.Printf("%+v\n", balance)
}
Get the list of All the models the API supports right now. This is different from what deepseek-go might support.
func ListModels() {
	client := deepseek.NewClient("DEEPSEEK_API_KEY")
	ctx := context.Background()
	models, err := deepseek.ListAllModels(client, ctx)
	if err != nil {
		t.Fatalf("Error listing models: %v", err)
	}
	fmt.Printf("\n%+v\n", models)
}

Getting a Deepseek Key

To use the Deepseek API, you need an API key. You can obtain one by signing up on the Deepseek website


Running Tests

Setup
  1. Copy the example environment file:

    cp .env.example .env
    
  2. Add your DeepSeek API key to .env:

    TEST_DEEPSEEK_API_KEY=your_api_key_here
    
  3. (Optional) Configure test timeout:

    # Default is 30s, increase for slower connections
    TEST_TIMEOUT=1m
    
Test Organization

The tests are organized into several files:

  • client_test.go: Client configuration and error handling
  • chat_test.go: Chat completion functionality
  • chat_stream_test.go: Chat streaming functionality
  • models_test.go: Model listing and retrieval
  • balance_test.go: Account balance operations
  • tokens_test.go: Token estimation utilities
  • errors_test.go: Tests the error handler
Running Tests
  1. Run all tests (requires API key):

    go test -v ./...
    
  2. Run tests in short mode (skips API calls):

    go test -v -short ./...
    
  3. Run tests with race detection:

    go test -v -race ./...
    
  4. Run tests with coverage:

    go test -v -coverprofile=coverage.txt -covermode=atomic ./...
    

    View coverage in browser:

    go tool cover -html=coverage.txt
    
  5. Run specific test:

    # Example: Run only chat completion tests
    go test -v -run TestCreateChatCompletion ./...
    

License

This project is licensed under the MIT License. See the LICENSE file for details.


Credits


Feel free to contribute, open issues, or submit PRs to help improve Deepseek-Go! Let us know if you encounter any issues.

Documentation

Index

Constants

View Source
const (
	DeepSeekChat     = "deepseek-chat"
	DeepSeekCoder    = "deepseek-coder"
	DeepSeekReasoner = "deepseek-reasoner"
)
View Source
const BaseURL string = "https://api.deepseek.com/v1"

Variables

View Source
var (
	ErrChatCompletionStreamNotSupported = errors.New("streaming is not supported with this method")
	ErrUnexpectedResponseFormat         = errors.New("unexpected response format")
)

Functions

func HandleAPIError

func HandleAPIError(resp *http.Response) error

Types

type APIError

type APIError struct {
	StatusCode    int    // HTTP status code
	APICode       int    // Business error code from API response
	Message       string // Human-readable error message
	OriginalError error  // Wrapped error for debugging
	ResponseBody  string // Raw JSON response body
}

func (APIError) Error

func (e APIError) Error() string

type APIModels added in v0.1.1

type APIModels struct {
	Object string  `json:"object"` //Object (string)
	Data   []Model `json:"data"`   // List of Models
}

func ListAllModels added in v0.1.1

func ListAllModels(c *Client, ctx context.Context) (*APIModels, error)

Models supported by the API itself

type APIType

type APIType string

type BalanceInfo

type BalanceInfo struct {
	Currency        string `json:"currency"`          //The currency of the balance.
	TotalBalance    string `json:"total_balance"`     //The total available balance, including the granted balance and the topped-up balance.
	GrantedBalance  string `json:"granted_balance"`   //The total not expired granted balance.
	ToppedUpBalance string `json:"topped_up_balance"` //The total topped-up balance.
}

type BalanceResponse

type BalanceResponse struct {
	IsAvailable  bool          `json:"is_available"`  //Whether the user's balance is sufficient for API calls.
	BalanceInfos []BalanceInfo `json:"balance_infos"` //List of Balance infos
}

func GetBalance

func GetBalance(c *Client, ctx context.Context) (*BalanceResponse, error)

type ChatCompletionMessage

type ChatCompletionMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

func MapMessageToChatCompletionMessage

func MapMessageToChatCompletionMessage(m handlers.Message) (ChatCompletionMessage, error)

type ChatCompletionRequest

type ChatCompletionRequest struct {
	Model            string                  `json:"model"`                       // Required: Model ID, e.g., "deepseek-chat"
	Messages         []ChatCompletionMessage `json:"messages"`                    // Required: List of messages
	FrequencyPenalty float32                 `json:"frequency_penalty,omitempty"` // Optional: Frequency penalty, >= -2 and <= 2
	MaxTokens        int                     `json:"max_tokens,omitempty"`        // Optional: Maximum tokens, > 1
	PresencePenalty  float32                 `json:"presence_penalty,omitempty"`  // Optional: Presence penalty, >= -2 and <= 2
	Temperature      float32                 `json:"temperature,omitempty"`       // Optional: Sampling temperature, <= 2
	TopP             float32                 `json:"top_p,omitempty"`             // Optional: Nucleus sampling parameter, <= 1
	ResponseFormat   *ResponseFormat         `json:"response_format,omitempty"`   // Optional: Custom response format
	Stop             []string                `json:"stop,omitempty"`              // Optional: Stop signals
	Tools            []Tools                 `json:"tools,omitempty"`             // Optional: List of tools
	LogProbs         bool                    `json:"logprobs,omitempty"`          // Optional: Enable log probabilities
	TopLogProbs      int                     `json:"top_logprobs,omitempty"`      // Optional: Number of top tokens with log probabilities, <= 20
}

make a different struct for streaming with streaming options parameter

type ChatCompletionStream

type ChatCompletionStream interface {
	Recv() (*StreamChatCompletionResponse, error)
	Close() error
}

type Client

type Client struct {
	AuthToken string
	BaseURL   string
}

func NewClient

func NewClient(AuthToken string) *Client

NewClient creates a new client with an authentication token.

func (*Client) CreateChatCompletion

func (c *Client) CreateChatCompletion(
	ctx context.Context,
	request *ChatCompletionRequest,
) (*handlers.ChatCompletionResponse, error)

CreateChatCompletion sends a chat completion request and returns the generated response.

func (*Client) CreateChatCompletionStream

func (c *Client) CreateChatCompletionStream(
	ctx context.Context,
	request *StreamChatCompletionRequest,
) (ChatCompletionStream, error)

CreateStreamChatCompletion send a chat completion request with stream = true and returns the delta

type ClientConfig

type ClientConfig struct {
	AuthToken  string
	BaseURL    string
	HTTPClient HTTPDoer
}

func DefaultConfig

func DefaultConfig(AuthToken string) ClientConfig

type Function

type Function struct {
	Name        string      `json:"name"`                 // The name of the function (required)
	Description string      `json:"description"`          // Description of the function (required)
	Parameters  *Parameters `json:"parameters,omitempty"` // Parameters schema (optional)
}

Function defines the structure of a function tool

type HTTPDoer

type HTTPDoer interface {
	Do(req *http.Request) (*http.Response, error)
}

type Model added in v0.1.1

type Model struct {
	ID      string `json:"id"`       //The id of the model (string)
	Object  string `json:"object"`   //The object of the model (string)
	OwnedBy string `json:"owned_by"` //The owner of the model(usally deepseek)
}

type Parameters

type Parameters struct {
	Type       string                 `json:"type"` // Type of the parameters, e.g., "object" (required)
	Properties map[string]interface{} `json:"properties,omitempty"`
	Required   []string               `json:"required,omitempty"`
}

type ResponseFormat

type ResponseFormat struct {
	Type string `json:"type"` //either text or json_object. If json_object, please mention "json" anywhere in your prompt.
}

type StreamChatCompletionMessage

type StreamChatCompletionMessage struct {
	Role    string `json:"role"`
	Content string `json:"content"`
}

type StreamChatCompletionRequest

type StreamChatCompletionRequest struct {
	Stream           bool                    `json:"stream,omitempty"`            //Comments: Defaults to true, since it's "STREAM"
	Model            string                  `json:"model"`                       // Required: Model ID, e.g., "deepseek-chat"
	Messages         []ChatCompletionMessage `json:"messages"`                    // Required: List of messages
	FrequencyPenalty float32                 `json:"frequency_penalty,omitempty"` // Optional: Frequency penalty, >= -2 and <= 2
	MaxTokens        int                     `json:"max_tokens,omitempty"`        // Optional: Maximum tokens, > 1
	PresencePenalty  float32                 `json:"presence_penalty,omitempty"`  // Optional: Presence penalty, >= -2 and <= 2
	Temperature      float32                 `json:"temperature,omitempty"`       // Optional: Sampling temperature, <= 2
	TopP             float32                 `json:"top_p,omitempty"`             // Optional: Nucleus sampling parameter, <= 1
	ResponseFormat   *ResponseFormat         `json:"response_format,omitempty"`   // Optional: Custom response format: just don't try, it breaks rn ;)
	Stop             []string                `json:"stop,omitempty"`              // Optional: Stop signals
	Tools            []Tools                 `json:"tools,omitempty"`             // Optional: List of tools
	LogProbs         bool                    `json:"logprobs,omitempty"`          // Optional: Enable log probabilities
	TopLogProbs      int                     `json:"top_logprobs,omitempty"`      // Optional: Number of top tokens with log probabilities, <= 20
}

type StreamChatCompletionResponse

type StreamChatCompletionResponse struct {
	ID      string          `json:"id"`
	Object  string          `json:"object"`
	Created int64           `json:"created"`
	Model   string          `json:"model"`
	Choices []StreamChoices `json:"choices"`
	Usage   *StreamUsage    `json:"usage,omitempty"`
}

type StreamChoices

type StreamChoices struct {
	Index        int `json:"index"`
	Delta        StreamDelta
	FinishReason string `json:"finish_reason"`
}

type StreamDelta

type StreamDelta struct {
	Role    string `json:"role,omitempty"`
	Content string `json:"content"`
}

type StreamOptions

type StreamOptions struct {
	IncludeUsage bool
}

type StreamUsage

type StreamUsage struct {
	PromptTokens     int `json:"prompt_tokens"`
	CompletionTokens int `json:"completion_tokens"`
	TotalTokens      int `json:"total_tokens"`
}

type TokenEstimate added in v0.1.1

type TokenEstimate struct {
	EstimatedTokens int `json:"estimated_tokens"` //the total estimated prompt tokens. These are different form total tokens used.
}

TokenEstimate represents an estimated token count

func EstimateTokenCount added in v0.1.1

func EstimateTokenCount(text string) *TokenEstimate

EstimateTokenCount estimates the number of tokens in a text based on character type ratios

func EstimateTokensFromMessages added in v0.1.1

func EstimateTokensFromMessages(messages *ChatCompletionRequest) *TokenEstimate

EstimateTokensFromMessages estimates the number of tokens in a list of chat messages

type Tools

type Tools struct {
	Type     string   `json:"type"`     // Type of the tool, e.g., "function" (required)
	Function Function `json:"function"` // The function details (required)
}

Tool defines the structure for a tool

Directories

Path Synopsis
internal
testutil
Package testutil provides testing utilities for the DeepSeek client.
Package testutil provides testing utilities for the DeepSeek client.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL