Commit 64c53496 authored by Wade's avatar Wade

deepseek plugins ok

parent 227400bd
module github.com/wade-liwei/agentchat
go 1.24.1
require (
github.com/cohesion-org/deepseek-go v1.3.1
github.com/firebase/genkit/go v0.5.4
)
require (
cloud.google.com/go v0.118.0 // indirect
cloud.google.com/go/auth v0.16.0 // indirect
cloud.google.com/go/compute/metadata v0.6.0 // indirect
github.com/bahlo/generic-list-go v0.2.0 // indirect
github.com/blues/jsonata-go v1.5.4 // indirect
github.com/buger/jsonparser v1.1.1 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/go-logr/logr v1.4.2 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/goccy/go-yaml v1.17.1 // indirect
github.com/google/dotprompt/go v0.0.0-20250424065700-61c578cf43ac // indirect
github.com/google/go-cmp v0.7.0 // indirect
github.com/google/s2a-go v0.1.9 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/googleapis/enterprise-certificate-proxy v0.3.6 // indirect
github.com/googleapis/gax-go/v2 v2.14.1 // indirect
github.com/gorilla/websocket v1.5.3 // indirect
github.com/invopop/jsonschema v0.13.0 // indirect
github.com/joho/godotenv v1.5.1 // indirect
github.com/mailru/easyjson v0.9.0 // indirect
github.com/mbleigh/raymond v0.0.0-20250414171441-6b3a58ab9e0a // indirect
github.com/ollama/ollama v0.6.5 // indirect
github.com/wk8/go-ordered-map/v2 v2.1.8 // indirect
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect
github.com/xeipuuv/gojsonschema v1.2.0 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
go.opentelemetry.io/otel v1.35.0 // indirect
go.opentelemetry.io/otel/metric v1.35.0 // indirect
go.opentelemetry.io/otel/sdk v1.35.0 // indirect
go.opentelemetry.io/otel/trace v1.35.0 // indirect
golang.org/x/crypto v0.37.0 // indirect
golang.org/x/exp v0.0.0-20250408133849-7e4ce0ab07d0 // indirect
golang.org/x/net v0.39.0 // indirect
golang.org/x/sys v0.32.0 // indirect
golang.org/x/text v0.24.0 // indirect
google.golang.org/genai v1.5.0 // indirect
google.golang.org/genproto/googleapis/rpc v0.0.0-20250414145226-207652e42e2e // indirect
google.golang.org/grpc v1.72.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
This diff is collapsed.
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"fmt"
"log"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
"github.com/wade-liwei/agentchat/plugins/deepseek"
)
func main() {
ctx := context.Background()
ds := deepseek.DeepSeek{
APIKey:"sk-9f70df871a7c4b8aa566a3c7a0603706",
}
g, err := genkit.Init(ctx, genkit.WithPlugins(&ds))
if err != nil {
log.Fatal(err)
}
m :=ds.DefineModel(g,
deepseek.ModelDefinition{
Name: "deepseek-chat", // Choose an appropriate model
Type: "chat", // Must be chat for tool support
},
nil)
// Define a simple flow that generates jokes about a given topic
//genkit.DefineFlow(g, "jokesFlow", func(ctx context.Context, input string) (string, error) {
resp, err := genkit.Generate(ctx, g,
ai.WithModel(m),
ai.WithPrompt(`Tell silly short jokes about apple`))
if err != nil{
fmt.Println(err.Error())
return
}
fmt.Println("resp.Text()",resp.Text())
// if err != nil {
// return "", err
// }
// text := resp.Text()
// return text, nil
// })
//<-ctx.Done()
}
This diff is collapsed.
// Copyright 2025 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
// Package deepseek provides a Genkit plugin for DeepSeek's API using the go-deepseek client.
package deepseek
import (
"context"
"fmt"
"log"
"strings"
"sync"
"github.com/firebase/genkit/go/ai"
"github.com/firebase/genkit/go/genkit"
deepseek "github.com/cohesion-org/deepseek-go"
// "github.com/go-deepseek/deepseek"
// "github.com/go-deepseek/deepseek/request"
// "github.com/go-deepseek/deepseek/response"
)
const provider = "deepseek"
var (
mediaSupportedModels = []string{deepseek.DeepSeekChat,deepseek.DeepSeekCoder,deepseek.DeepSeekReasoner}
// toolSupportedModels = []string{
// "qwq", "mistral-small3.1", "llama3.3", "llama3.2", "llama3.1", "mistral",
// "qwen2.5", "qwen2.5-coder", "qwen2", "mistral-nemo", "mixtral", "smollm2",
// "mistral-small", "command-r", "hermes3", "mistral-large", "command-r-plus",
// "phi4-mini", "granite3.1-dense", "granite3-dense", "granite3.2", "athene-v2",
// "nemotron-mini", "nemotron", "llama3-groq-tool-use", "aya-expanse", "granite3-moe",
// "granite3.2-vision", "granite3.1-moe", "cogito", "command-r7b", "firefunction-v2",
// "granite3.3", "command-a", "command-r7b-arabic",
// }
roleMapping = map[ai.Role]string{
ai.RoleUser: deepseek.ChatMessageRoleUser,
ai.RoleModel: deepseek.ChatMessageRoleAssistant,
ai.RoleSystem: deepseek.ChatMessageRoleSystem,
ai.RoleTool: deepseek.ChatMessageRoleTool,
}
)
// DeepSeek holds configuration for the plugin.
type DeepSeek struct {
APIKey string // DeepSeek API key
//ServerAddress string
mu sync.Mutex // Mutex to control access.
initted bool // Whether the plugin has been initialized.
}
// Name returns the provider name.
func (d DeepSeek) Name() string {
return provider
}
// Init initializes the plugin.
// Since Ollama models are locally hosted, the plugin doesn't initialize any default models.
// After downloading a model, call [DefineModel] to use it.
// func (o *DeepSeek) Init(ctx context.Context, g *genkit.Genkit) (err error) {
// o.mu.Lock()
// defer o.mu.Unlock()
// if o.initted {
// panic("deepseek.Init already called")
// }
// if o == nil || o.APIKey == "" {
// return errors.New("deepseek : need api key")
// }
// o.initted = true
// return nil
// }
// ModelDefinition represents a model with its name and type.
type ModelDefinition struct {
Name string
Type string
}
// // DefineModel defines a DeepSeek model in Genkit.
func (d *DeepSeek) DefineModel(g *genkit.Genkit, model ModelDefinition, info *ai.ModelInfo) ai.Model {
d.mu.Lock()
defer d.mu.Unlock()
if !d.initted {
panic("deepseek.Init not called")
}
// Define model info, supporting multiturn and system role.
mi := ai.ModelInfo{
Label: model.Name,
Supports: &ai.ModelSupports{
Multiturn: true,
SystemRole: true,
Media: false, // DeepSeek API primarily supports text.
Tools: false, // Tools not yet supported in this implementation.
},
Versions: []string{},
}
if info != nil {
mi = *info
}
meta := &ai.ModelInfo{
// Label: "DeepSeek - " + model.Name,
Label: model.Name,
Supports: mi.Supports,
Versions: []string{},
}
gen := &generator{model: model, apiKey: d.APIKey}
return genkit.DefineModel(g, provider, model.Name, meta, gen.generate)
}
// // IsDefinedModel reports whether a model is defined.
// func IsDefinedModel(g *genkit.Genkit, name string) bool {
// return genkit.LookupModel(g, provider, name) != nil
// }
// // Model returns the ai.Model with the given name.
// func Model(g *genkit.Genkit, name string) ai.Model {
// return genkit.LookupModel(g, provider, name)
// }
// Init initializes the DeepSeek plugin.
func (d *DeepSeek) Init(ctx context.Context, g *genkit.Genkit) error {
d.mu.Lock()
defer d.mu.Unlock()
if d.initted {
panic("deepseek.Init already called")
}
if d == nil || d.APIKey == "" {
return fmt.Errorf("deepseek: need APIKey")
}
d.initted = true
// Define default models.
// defaultModels := []ModelDefinition{
// {Name: deepseek.DeepSeekChat, Type: "chat"}, // deepseek-chat
// {Name: deepseek.DeepSeekReasoner, Type: "chat"}, // deepseek-reasoner
// }
// for _, model := range defaultModels {
// d.DefineModel(g, model, nil)
// }
return nil
}
// generator handles model generation.
type generator struct {
model ModelDefinition
apiKey string
}
// generate implements the Genkit model generation interface.
func (g *generator) generate(ctx context.Context, input *ai.ModelRequest, cb func(context.Context, *ai.ModelResponseChunk) error) (*ai.ModelResponse, error) {
// stream := cb != nil
if len(input.Messages) == 0 {
return nil, fmt.Errorf("prompt or messages required")
}
// Set up the Deepseek client
// Initialize DeepSeek client.
client := deepseek.NewClient(g.apiKey)
// Create a chat completion request
request := &deepseek.ChatCompletionRequest{
Model: g.model.Name,
}
for _, msg := range input.Messages {
role, ok := roleMapping[msg.Role]
if !ok {
return nil, fmt.Errorf("unsupported role: %s", msg.Role)
}
content := concatMessageParts(msg.Content)
request.Messages = append(request.Messages, deepseek.ChatCompletionMessage{
Role: role,
Content: content,
})
}
// Send the request and handle the response
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)
// Create a final response with the merged chunks
finalResponse := &ai.ModelResponse{
Request: input,
FinishReason: ai.FinishReason("stop"),
Message: &ai.Message{
Role: ai.RoleModel,
},
}
for _, chunk := range response.Choices {
p := ai.Part{
Text: chunk.Message.Content,
Kind: ai.PartKind(chunk.Index),
}
finalResponse.Message.Content = append(finalResponse.Message.Content,&p)
}
return finalResponse, nil // Return the final merged response
}
// concatMessageParts concatenates message parts into a single string.
func concatMessageParts(parts []*ai.Part) string {
var sb strings.Builder
for _, part := range parts {
if part.IsText() {
sb.WriteString(part.Text)
}
// Ignore non-text parts (e.g., media, tools) as DeepSeek API doesn't support them.
}
return sb.String()
}
/*
// Choice represents a completion choice generated by the model.
type Choice struct {
Index int `json:"index"` // Index of the choice in the list of choices.
Message Message `json:"message"` // The message generated by the model.
Logprobs any `json:"logprobs,omitempty"` // Log probabilities of the tokens, if available. // Changed to any as of April 21 2025 because the logprobs field is sometimes a flot64 and sometimes a Logprobs struct.
FinishReason string `json:"finish_reason"` // Reason why the completion finished.
}
// A Part is one part of a [Document]. This may be plain text or it
// may be a URL (possibly a "data:" URL with embedded data).
type Part struct {
Kind PartKind `json:"kind,omitempty"`
ContentType string `json:"contentType,omitempty"` // valid for kind==blob
Text string `json:"text,omitempty"` // valid for kind∈{text,blob}
ToolRequest *ToolRequest `json:"toolRequest,omitempty"` // valid for kind==partToolRequest
ToolResponse *ToolResponse `json:"toolResponse,omitempty"` // valid for kind==partToolResponse
Custom map[string]any `json:"custom,omitempty"` // valid for plugin-specific custom parts
Metadata map[string]any `json:"metadata,omitempty"` // valid for all kinds
}
*/
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment