Adding Ollama as a conversation component for local dev/running of LLMs (#3688)

Signed-off-by: Emmanuel Auffray <emmanuel.auffray@gmail.com>
Co-authored-by: Mike Nguyen <hey@mike.ee>
Co-authored-by: Yaron Schneider <schneider.yaron@live.com>
This commit is contained in:
Emmanuel Auffray 2025-03-24 05:38:17 +13:00 committed by GitHub
parent b348969d81
commit 47947d8770
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 150 additions and 0 deletions

View File

@ -0,0 +1,23 @@
# yaml-language-server: $schema=../../../component-metadata-schema.json
schemaVersion: v1
type: conversation
name: ollama
version: v1
status: alpha
title: "Ollama"
urls:
- title: Reference
url: https://docs.dapr.io/reference/components-reference/supported-conversation/ollama/
metadata:
- name: model
required: false
description: |
The Ollama LLM to use. Defaults to llama3.2:latest
type: string
example: 'llama3.2:latest'
- name: cacheTTL
required: false
description: |
A time-to-live value for a prompt cache to expire. Uses Golang durations
type: string
example: '10m'

View File

@ -0,0 +1,127 @@
/*
Copyright 2024 The Dapr Authors
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 ollama
import (
"context"
"reflect"
"github.com/dapr/components-contrib/conversation"
"github.com/dapr/components-contrib/metadata"
"github.com/dapr/kit/logger"
kmeta "github.com/dapr/kit/metadata"
"github.com/tmc/langchaingo/llms"
"github.com/tmc/langchaingo/llms/ollama"
)
type Ollama struct {
llm llms.Model
logger logger.Logger
}
func NewOllama(logger logger.Logger) conversation.Conversation {
o := &Ollama{
logger: logger,
}
return o
}
const defaultModel = "llama3.2:latest"
func (o *Ollama) Init(ctx context.Context, meta conversation.Metadata) error {
md := conversation.LangchainMetadata{}
err := kmeta.DecodeMetadata(meta.Properties, &md)
if err != nil {
return err
}
model := defaultModel
if md.Model != "" {
model = md.Model
}
llm, err := ollama.New(
ollama.WithModel(model),
)
if err != nil {
return err
}
o.llm = llm
if md.CacheTTL != "" {
cachedModel, cacheErr := conversation.CacheModel(ctx, md.CacheTTL, o.llm)
if cacheErr != nil {
return cacheErr
}
o.llm = cachedModel
}
return nil
}
func (o *Ollama) GetComponentMetadata() (metadataInfo metadata.MetadataMap) {
metadataStruct := conversation.LangchainMetadata{}
metadata.GetMetadataInfoFromStructType(reflect.TypeOf(metadataStruct), &metadataInfo, metadata.ConversationType)
return
}
func (o *Ollama) Converse(ctx context.Context, r *conversation.ConversationRequest) (res *conversation.ConversationResponse, err error) {
messages := make([]llms.MessageContent, 0, len(r.Inputs))
for _, input := range r.Inputs {
role := conversation.ConvertLangchainRole(input.Role)
messages = append(messages, llms.MessageContent{
Role: role,
Parts: []llms.ContentPart{
llms.TextPart(input.Message),
},
})
}
opts := []llms.CallOption{}
if r.Temperature > 0 {
opts = append(opts, conversation.LangchainTemperature(r.Temperature))
}
resp, err := o.llm.GenerateContent(ctx, messages, opts...)
if err != nil {
return nil, err
}
outputs := make([]conversation.ConversationResult, 0, len(resp.Choices))
for i := range resp.Choices {
outputs = append(outputs, conversation.ConversationResult{
Result: resp.Choices[i].Content,
Parameters: r.Parameters,
})
}
res = &conversation.ConversationResponse{
Outputs: outputs,
}
return res, nil
}
func (o *Ollama) Close() error {
return nil
}