Details (original message): Implement the backend: store/bus/runmgr core, mock and LLM runners (agent-native client), HITL three-branch routing, subagent runs, builtin and declarative HTTP tools, ingest parsers (md/txt/docx/pdf-text), knowledge fulltext, layered settings, profiles, plugin host, REST+SSE API, single-binary entrypoint; e2e and unit tests.
143 lines
4.6 KiB
Go
143 lines
4.6 KiB
Go
package store
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"time"
|
|
|
|
core "agent-system-core"
|
|
)
|
|
|
|
var ErrNotFound = errors.New("store: not found")
|
|
|
|
// ---- 项目与会话 ----
|
|
|
|
func (s *Store) CreateProject(name string) (core.Project, error) {
|
|
p := core.Project{ID: newID("prj"), Name: name, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC(), Settings: map[string]any{}}
|
|
_, err := s.db.Exec(`INSERT INTO projects(id,name,settings,created_at,updated_at) VALUES(?,?,?,?,?)`,
|
|
p.ID, p.Name, "{}", nowISO(), nowISO())
|
|
return p, err
|
|
}
|
|
|
|
func (s *Store) ListProjects() ([]core.Project, error) {
|
|
rows, err := s.db.Query(`SELECT id,name,settings,created_at,updated_at FROM projects ORDER BY updated_at DESC`)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []core.Project
|
|
for rows.Next() {
|
|
var p core.Project
|
|
var settings string
|
|
var ca, ua string
|
|
if err := rows.Scan(&p.ID, &p.Name, &settings, &ca, &ua); err != nil {
|
|
return nil, err
|
|
}
|
|
p.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
|
p.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
|
if settings != "" && settings != "{}" {
|
|
p.Settings, _ = unmarshalJSON[map[string]any](settings)
|
|
}
|
|
out = append(out, p)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (s *Store) GetProject(id string) (core.Project, error) {
|
|
var p core.Project
|
|
var settings, ca, ua string
|
|
err := s.db.QueryRow(`SELECT id,name,settings,created_at,updated_at FROM projects WHERE id=?`, id).
|
|
Scan(&p.ID, &p.Name, &settings, &ca, &ua)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return p, ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return p, err
|
|
}
|
|
p.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
|
p.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
|
if settings != "" && settings != "{}" {
|
|
p.Settings, _ = unmarshalJSON[map[string]any](settings)
|
|
}
|
|
return p, nil
|
|
}
|
|
|
|
func (s *Store) UpdateProjectSettings(id string, values map[string]any) error {
|
|
cur, err := s.GetProject(id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
merged := core.MergeSettings(cur.Settings, values)
|
|
_, err = s.db.Exec(`UPDATE projects SET settings=?, updated_at=? WHERE id=?`, marshalJSON(merged), nowISO(), id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) RenameProject(id, name string) error {
|
|
_, err := s.db.Exec(`UPDATE projects SET name=?, updated_at=? WHERE id=?`, name, nowISO(), id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) DeleteProject(id string) error {
|
|
_, err := s.db.Exec(`DELETE FROM projects WHERE id=?`, id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) CreateConversation(projectID, title, profileID string) (core.Conversation, error) {
|
|
c := core.Conversation{ID: newID("cnv"), ProjectID: projectID, Title: title, ProfileID: profileID, CreatedAt: time.Now().UTC(), UpdatedAt: time.Now().UTC()}
|
|
_, err := s.db.Exec(`INSERT INTO conversations(id,project_id,title,profile_id,created_at,updated_at) VALUES(?,?,?,?,?,?)`,
|
|
c.ID, c.ProjectID, c.Title, c.ProfileID, nowISO(), nowISO())
|
|
return c, err
|
|
}
|
|
|
|
func (s *Store) ListConversations(projectID string) ([]core.Conversation, error) {
|
|
rows, err := s.db.Query(`SELECT id,project_id,title,profile_id,created_at,updated_at FROM conversations WHERE project_id=? ORDER BY updated_at DESC`, projectID)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
defer rows.Close()
|
|
var out []core.Conversation
|
|
for rows.Next() {
|
|
var c core.Conversation
|
|
var ca, ua string
|
|
if err := rows.Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &ca, &ua); err != nil {
|
|
return nil, err
|
|
}
|
|
c.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
|
c.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
|
out = append(out, c)
|
|
}
|
|
return out, rows.Err()
|
|
}
|
|
|
|
func (s *Store) GetConversation(id string) (core.Conversation, error) {
|
|
var c core.Conversation
|
|
var ca, ua string
|
|
err := s.db.QueryRow(`SELECT id,project_id,title,profile_id,created_at,updated_at FROM conversations WHERE id=?`, id).
|
|
Scan(&c.ID, &c.ProjectID, &c.Title, &c.ProfileID, &ca, &ua)
|
|
if errors.Is(err, sql.ErrNoRows) {
|
|
return c, ErrNotFound
|
|
}
|
|
if err != nil {
|
|
return c, err
|
|
}
|
|
c.CreatedAt, _ = time.Parse(time.RFC3339Nano, ca)
|
|
c.UpdatedAt, _ = time.Parse(time.RFC3339Nano, ua)
|
|
return c, nil
|
|
}
|
|
|
|
func (s *Store) TouchConversation(id string) { s.db.Exec(`UPDATE conversations SET updated_at=? WHERE id=?`, nowISO(), id) }
|
|
|
|
func (s *Store) UpdateConversation(id, title, profileID string) error {
|
|
if title == "" {
|
|
_, err := s.db.Exec(`UPDATE conversations SET profile_id=?, updated_at=? WHERE id=?`, profileID, nowISO(), id)
|
|
return err
|
|
}
|
|
_, err := s.db.Exec(`UPDATE conversations SET title=?, profile_id=?, updated_at=? WHERE id=?`, title, profileID, nowISO(), id)
|
|
return err
|
|
}
|
|
|
|
func (s *Store) DeleteConversation(id string) error {
|
|
_, err := s.db.Exec(`DELETE FROM conversations WHERE id=?`, id)
|
|
return err
|
|
}
|