basic llm request stream

This commit is contained in:
fabien committed 2026-09-22 21:15:23 +02:00
1 parent f74e4d1043
commit a92cc76a9a
66 files changed
+1496 -158

No files matched your search

@@ -14,6 +14,7 @@ type Database interface {
FileRepository() domain.FileRepository
ProviderRepository() domain.ProviderRepository
AgentRepository() domain.AgentRepository
ChatRepository() domain.ChatRepository
Migrate() error
Close()
}
+7 -5
View File
@@ -41,12 +41,14 @@ func NewTable[T any](db *sql.DB, dialect Dialect, tablename string) (Table[T], e
fields := make([]dbfield, 0)
for field := range t.Fields() {
f := dbfield{
name: field.Name,
stype: field.Type.String(),
dbname: field.Tag.Get("db"),
if field.Tag.Get("db") != "-" {
f := dbfield{
name: field.Name,
stype: field.Type.String(),
dbname: field.Tag.Get("db"),
}
fields = append(fields, f)
}
fields = append(fields, f)
}
return Table[T]{
@@ -10,24 +10,24 @@ import (
type TursoAgentRepository struct {
db *sql.DB
AgentTable orm.Table[domain.Agent]
agentTable orm.Table[domain.Agent]
}
func NewTursoAgentRepository(db *sql.DB) *TursoAgentRepository {
dialect := orm.NewSqliteDialect()
AgentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents")
agentTable, err := orm.NewTable[domain.Agent](db, dialect, "agents")
if err != nil {
log.Fatalf("error creating lasebuche agent table")
}
return &TursoAgentRepository{
db: db,
AgentTable: AgentTable,
agentTable: agentTable,
}
}
func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*domain.Agent, error) {
return r.AgentTable.Select(
return r.agentTable.Select(
orm.WithWhere(
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
teamID,
@@ -37,7 +37,7 @@ func (r *TursoAgentRepository) ListAgents(userID string, teamID string) ([]*doma
}
func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string) (*domain.Agent, error) {
return r.AgentTable.SelectOne(
return r.agentTable.SelectOne(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
@@ -46,15 +46,15 @@ func (r *TursoAgentRepository) GetAgent(userID string, teamID string, id string)
}
func (r *TursoAgentRepository) CreateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
return r.AgentTable.Insert(agent)
return r.agentTable.Insert(agent)
}
func (r *TursoAgentRepository) UpdateAgent(userID string, agent *domain.Agent) (*domain.Agent, error) {
return r.AgentTable.Update(agent)
return r.agentTable.Update(agent)
}
func (r *TursoAgentRepository) DeleteAgent(userID string, teamID string, id string) error {
return r.AgentTable.DeleteWhere(
return r.agentTable.DeleteWhere(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
@@ -0,0 +1,83 @@
package turso
import (
"database/sql"
"log"
"trankilou.fr/lassistanoque/backend/internal/adapter/database/orm"
"trankilou.fr/lassistanoque/backend/internal/domain"
)
type TursoChatRepository struct {
db *sql.DB
chatTable orm.Table[domain.Chat]
messageTable orm.Table[domain.Message]
}
func NewTursoChatRepository(db *sql.DB) *TursoChatRepository {
dialect := orm.NewSqliteDialect()
chatTable, err := orm.NewTable[domain.Chat](db, dialect, "chats")
messageTable, err := orm.NewTable[domain.Message](db, dialect, "chat_messages")
if err != nil {
log.Fatalf("error creating lasebuche agent table")
}
return &TursoChatRepository{
db: db,
chatTable: chatTable,
messageTable: messageTable,
}
}
func (r *TursoChatRepository) ListChats(userID string, teamID string, page int) ([]*domain.Chat, error) {
return r.chatTable.Select(
orm.WithWhere(
"team_id=$1 and team_id in (select team_id from user_teams where user_id=$2)",
teamID,
userID,
),
orm.WithPagination(page, 50),
)
}
func (r *TursoChatRepository) GetChat(userID string, teamID string, id string) (*domain.Chat, error) {
return r.chatTable.SelectOne(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
userID,
)
}
func (r *TursoChatRepository) CreateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
return r.chatTable.Insert(chat)
}
func (r *TursoChatRepository) UpdateChat(userID string, chat *domain.Chat) (*domain.Chat, error) {
return r.chatTable.Update(chat)
}
func (r *TursoChatRepository) DeleteChat(userID string, teamID string, id string) error {
return r.chatTable.DeleteWhere(
"id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
id,
teamID,
userID,
)
}
func (r *TursoChatRepository) GetChatMessages(userID string, teamID string, chatID string) ([]*domain.Message, error) {
return r.messageTable.Select(
orm.WithWhere(
"chat_id=$1 and team_id=$2 and team_id in (select team_id from user_teams where user_id=$3)",
chatID,
teamID,
userID,
),
orm.WithOrder("_date_created asc"),
)
}
func (r *TursoChatRepository) CreateChatMessage(userID string, message *domain.Message) (*domain.Message, error) {
return r.messageTable.Insert(message)
}
@@ -75,3 +75,7 @@ func (db *TursoDB) ProviderRepository() domain.ProviderRepository {
func (db *TursoDB) AgentRepository() domain.AgentRepository {
return NewTursoAgentRepository(db.DB)
}
func (db *TursoDB) ChatRepository() domain.ChatRepository {
return NewTursoChatRepository(db.DB)
}
@@ -9,6 +9,7 @@ drop table user_teams;
drop table channels;
drop table tools;
drop table tasks;
drop table history;
drop table chats;
drop table chat_contents;
drop table files;
drop table agents;
@@ -117,14 +117,30 @@ create table tasks (
_version text not null
);
create table history (
create table chats (
id text not null primary key,
task_id text not null,
start_datetime numeric,
team_id text not null,
start_datetime numeric not null,
end_datetime numeric,
prompt text not null,
log text not null,
response text not null,
agent_id text,
user_id text,
task_id text,
title text,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
);
create table chat_messages (
id text not null primary key,
team_id text not null,
chat_id text not null,
content_datetime numeric,
role text not null,
content text,
agent_id text,
tool_call_id text,
tool_calls text,
_date_created numeric not null default current_timestamp,
_date_updated numeric,
_version text not null
+43 -11
View File
@@ -5,6 +5,7 @@ import (
"fmt"
anyllm "github.com/mozilla-ai/any-llm-go"
"github.com/mozilla-ai/any-llm-go/providers"
"github.com/mozilla-ai/any-llm-go/providers/anthropic"
"github.com/mozilla-ai/any-llm-go/providers/ollama"
"github.com/mozilla-ai/any-llm-go/providers/openai"
@@ -79,13 +80,13 @@ func (e *AnyLLMEngine) Stream(
ctx context.Context,
provider *domain.Provider,
modelID string,
params *domain.StreamParams,
params *domain.LLMParams,
messages []*domain.Message,
) error {
) (*domain.Message, error) {
p, err := providerFactory(provider)
if err != nil {
return err
return nil, err
}
anyllmMessages := make([]anyllm.Message, 0)
@@ -96,26 +97,57 @@ func (e *AnyLLMEngine) Stream(
})
}
if params.ReasoningEffort == "" {
params.ReasoningEffort = "none"
}
chunkChan, errChan := p.CompletionStream(ctx, anyllm.CompletionParams{
Model: modelID,
Messages: anyllmMessages,
Stream: true,
Model: modelID,
Messages: anyllmMessages,
Stream: true,
ReasoningEffort: providers.ReasoningEffort(params.ReasoningEffort),
})
fullContent := ""
role := anyllm.RoleAssistant
for chunk := range chunkChan {
if len(chunk.Choices) > 0 {
content := chunk.Choices[0].Delta.Content
reasoning := chunk.Choices[0].Delta.Reasoning
if params.OnChunk != nil {
params.OnChunk(&domain.Chunk{
Content: content,
})
if content != "" {
fullContent += content
params.OnChunk(&domain.Chunk{
Done: false,
Role: role,
Content: content,
Reasoning: false,
})
} else if reasoning != nil {
params.OnChunk(&domain.Chunk{
Done: false,
Role: role,
Content: reasoning.Content,
Reasoning: true,
})
}
}
}
}
params.OnChunk(&domain.Chunk{
Done: true,
Role: role,
Content: "",
})
if err := <-errChan; err != nil {
return err
return nil, err
}
return nil
return &domain.Message{
Role: role,
Content: fullContent,
}, nil
}