refactor(store,tools): migrate IDs from UUID to bigserial int64
Some checks failed
CI / build-and-test (push) Failing after -31m12s
Some checks failed
CI / build-and-test (push) Failing after -31m12s
All internal entity lookups now use bigserial primary keys (int64) while GUIDs are retained for external/public identification. Updated store functions (TouchProject, UpdateThoughtMetadata, AddThoughtAttachment) to query by id instead of guid, added GetThoughtByID, changed semanticSearch and all tool helpers to use *int64 project IDs, and updated retry/backfill workers to use int64 thought IDs throughout.
This commit is contained in:
@@ -2,11 +2,11 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
@@ -53,11 +53,12 @@ func NewBackfillTool(db *store.DB, embeddings *ai.EmbeddingRunner, sessions *ses
|
||||
|
||||
// QueueThought queues a single thought for background embedding generation.
|
||||
// It is used by capture when the embedding provider is temporarily unavailable.
|
||||
func (t *BackfillTool) QueueThought(ctx context.Context, id uuid.UUID, content string) {
|
||||
func (t *BackfillTool) QueueThought(ctx context.Context, id int64, content string) {
|
||||
go func() {
|
||||
started := time.Now()
|
||||
idStr := fmt.Sprint(id)
|
||||
t.logger.Info("background embedding started",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", t.embeddings.PrimaryProvider()),
|
||||
slog.String("model", t.embeddings.PrimaryModel()),
|
||||
)
|
||||
@@ -65,7 +66,7 @@ func (t *BackfillTool) QueueThought(ctx context.Context, id uuid.UUID, content s
|
||||
result, err := t.embeddings.Embed(ctx, content)
|
||||
if err != nil {
|
||||
t.logger.Warn("background embedding error",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", t.embeddings.PrimaryProvider()),
|
||||
slog.String("model", t.embeddings.PrimaryModel()),
|
||||
slog.String("stage", "embed"),
|
||||
@@ -76,7 +77,7 @@ func (t *BackfillTool) QueueThought(ctx context.Context, id uuid.UUID, content s
|
||||
}
|
||||
if err := t.store.UpsertEmbedding(ctx, id, result.Model, result.Vector); err != nil {
|
||||
t.logger.Warn("background embedding error",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", t.embeddings.PrimaryProvider()),
|
||||
slog.String("model", result.Model),
|
||||
slog.String("stage", "upsert"),
|
||||
@@ -86,7 +87,7 @@ func (t *BackfillTool) QueueThought(ctx context.Context, id uuid.UUID, content s
|
||||
return
|
||||
}
|
||||
t.logger.Info("background embedding complete",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", t.embeddings.PrimaryProvider()),
|
||||
slog.String("model", result.Model),
|
||||
slog.Duration("duration", time.Since(started)),
|
||||
@@ -105,9 +106,9 @@ func (t *BackfillTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in
|
||||
return nil, BackfillOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
primaryModel := t.embeddings.PrimaryModel()
|
||||
@@ -140,24 +141,25 @@ func (t *BackfillTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in
|
||||
break
|
||||
}
|
||||
wg.Add(1)
|
||||
go func(id uuid.UUID, content string) {
|
||||
go func(id int64, content string) {
|
||||
defer wg.Done()
|
||||
defer sem.Release(1)
|
||||
|
||||
idStr := fmt.Sprint(id)
|
||||
result, embedErr := t.embeddings.Embed(ctx, content)
|
||||
if embedErr != nil {
|
||||
mu.Lock()
|
||||
out.Failures = append(out.Failures, BackfillFailure{ID: id.String(), Error: embedErr.Error()})
|
||||
out.Failures = append(out.Failures, BackfillFailure{ID: idStr, Error: embedErr.Error()})
|
||||
mu.Unlock()
|
||||
t.logger.Warn("backfill embed failed", slog.String("thought_id", id.String()), slog.String("error", embedErr.Error()))
|
||||
t.logger.Warn("backfill embed failed", slog.String("thought_id", idStr), slog.String("error", embedErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
if upsertErr := t.store.UpsertEmbedding(ctx, id, result.Model, result.Vector); upsertErr != nil {
|
||||
mu.Lock()
|
||||
out.Failures = append(out.Failures, BackfillFailure{ID: id.String(), Error: upsertErr.Error()})
|
||||
out.Failures = append(out.Failures, BackfillFailure{ID: idStr, Error: upsertErr.Error()})
|
||||
mu.Unlock()
|
||||
t.logger.Warn("backfill upsert failed", slog.String("thought_id", id.String()), slog.String("error", upsertErr.Error()))
|
||||
t.logger.Warn("backfill upsert failed", slog.String("thought_id", idStr), slog.String("error", upsertErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/ai"
|
||||
@@ -17,13 +16,13 @@ import (
|
||||
|
||||
// EmbeddingQueuer queues a thought for background embedding generation.
|
||||
type EmbeddingQueuer interface {
|
||||
QueueThought(ctx context.Context, id uuid.UUID, content string)
|
||||
QueueThought(ctx context.Context, id int64, content string)
|
||||
}
|
||||
|
||||
// MetadataQueuer queues a thought for background metadata retry. Both
|
||||
// MetadataRetryer and EnrichmentRetryer satisfy this.
|
||||
type MetadataQueuer interface {
|
||||
QueueThought(id uuid.UUID)
|
||||
QueueThought(id int64)
|
||||
}
|
||||
|
||||
type CaptureTool struct {
|
||||
@@ -66,7 +65,7 @@ func (t *CaptureTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in C
|
||||
Metadata: rawMetadata,
|
||||
}
|
||||
if project != nil {
|
||||
thought.ProjectID = &project.ID
|
||||
thought.ProjectID = &project.NumericID
|
||||
}
|
||||
|
||||
created, err := t.store.InsertThought(ctx, thought, t.embeddings.PrimaryModel())
|
||||
@@ -74,7 +73,7 @@ func (t *CaptureTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in C
|
||||
return nil, CaptureOutput{}, err
|
||||
}
|
||||
if project != nil {
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
if t.retryer != nil {
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/session"
|
||||
@@ -64,7 +63,7 @@ func (t *ChatHistoryTool) SaveChatHistory(ctx context.Context, req *mcp.CallTool
|
||||
h.Metadata = map[string]any{}
|
||||
}
|
||||
if project != nil {
|
||||
h.ProjectID = &project.ID
|
||||
h.ProjectID = &project.NumericID
|
||||
}
|
||||
|
||||
saved, err := t.store.SaveChatHistory(ctx, h)
|
||||
@@ -77,7 +76,7 @@ func (t *ChatHistoryTool) SaveChatHistory(ctx context.Context, req *mcp.CallTool
|
||||
// get_chat_history
|
||||
|
||||
type GetChatHistoryInput struct {
|
||||
ID string `json:"id,omitempty" jsonschema:"UUID of the saved chat history"`
|
||||
ID string `json:"id,omitempty" jsonschema:"numeric id of the saved chat history"`
|
||||
SessionID string `json:"session_id,omitempty" jsonschema:"original session_id — returns the most recent history for that session"`
|
||||
}
|
||||
|
||||
@@ -91,9 +90,9 @@ func (t *ChatHistoryTool) GetChatHistory(ctx context.Context, _ *mcp.CallToolReq
|
||||
}
|
||||
|
||||
if in.ID != "" {
|
||||
id, err := uuid.Parse(in.ID)
|
||||
id, err := parseID(in.ID)
|
||||
if err != nil {
|
||||
return nil, GetChatHistoryOutput{}, errInvalidField("id", "invalid id", "must be a valid UUID")
|
||||
return nil, GetChatHistoryOutput{}, errInvalidField("id", "invalid id", "must be a valid numeric id")
|
||||
}
|
||||
h, found, err := t.store.GetChatHistory(ctx, id)
|
||||
if err != nil {
|
||||
@@ -145,7 +144,7 @@ func (t *ChatHistoryTool) ListChatHistories(ctx context.Context, req *mcp.CallTo
|
||||
return nil, ListChatHistoriesOutput{}, err
|
||||
}
|
||||
if project != nil {
|
||||
filter.ProjectID = &project.ID
|
||||
filter.ProjectID = &project.NumericID
|
||||
}
|
||||
}
|
||||
|
||||
@@ -162,7 +161,7 @@ func (t *ChatHistoryTool) ListChatHistories(ctx context.Context, req *mcp.CallTo
|
||||
// delete_chat_history
|
||||
|
||||
type DeleteChatHistoryInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"UUID of the chat history to delete"`
|
||||
ID string `json:"id" jsonschema:"numeric id of the chat history to delete"`
|
||||
}
|
||||
|
||||
type DeleteChatHistoryOutput struct {
|
||||
@@ -170,7 +169,11 @@ type DeleteChatHistoryOutput struct {
|
||||
}
|
||||
|
||||
func (t *ChatHistoryTool) DeleteChatHistory(ctx context.Context, _ *mcp.CallToolRequest, in DeleteChatHistoryInput) (*mcp.CallToolResult, DeleteChatHistoryOutput, error) {
|
||||
deleted, err := t.store.DeleteChatHistory(ctx, in.ID)
|
||||
id, err := parseID(in.ID)
|
||||
if err != nil {
|
||||
return nil, DeleteChatHistoryOutput{}, errInvalidField("id", "invalid id", "must be a valid numeric id")
|
||||
}
|
||||
deleted, err := t.store.DeleteChatHistory(ctx, id)
|
||||
if err != nil {
|
||||
return nil, DeleteChatHistoryOutput{}, err
|
||||
}
|
||||
|
||||
@@ -52,7 +52,7 @@ func (t *ContextTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in P
|
||||
}
|
||||
|
||||
limit := normalizeLimit(in.Limit, t.search)
|
||||
recent, err := t.store.RecentThoughts(ctx, &project.ID, limit, 0)
|
||||
recent, err := t.store.RecentThoughts(ctx, &project.NumericID, limit, 0)
|
||||
if err != nil {
|
||||
return nil, ProjectContextOutput{}, err
|
||||
}
|
||||
@@ -60,7 +60,7 @@ func (t *ContextTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in P
|
||||
items := make([]ContextItem, 0, limit*2)
|
||||
seen := map[string]struct{}{}
|
||||
for _, thought := range recent {
|
||||
key := thought.ID.String()
|
||||
key := fmt.Sprint(thought.ID)
|
||||
seen[key] = struct{}{}
|
||||
items = append(items, ContextItem{
|
||||
ID: key,
|
||||
@@ -72,12 +72,12 @@ func (t *ContextTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in P
|
||||
|
||||
query := strings.TrimSpace(in.Query)
|
||||
if query != "" {
|
||||
semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, &project.ID, nil)
|
||||
semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, &project.NumericID, nil)
|
||||
if err != nil {
|
||||
return nil, ProjectContextOutput{}, err
|
||||
}
|
||||
for _, result := range semantic {
|
||||
key := result.ID.String()
|
||||
key := fmt.Sprint(result.ID)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -97,7 +97,7 @@ func (t *ContextTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in P
|
||||
lines = append(lines, thoughtContextLine(i, item.Content, item.Metadata, item.Similarity))
|
||||
}
|
||||
contextBlock := formatContextBlock(fmt.Sprintf("Project context for %s", project.Name), lines)
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
|
||||
return nil, ProjectContextOutput{
|
||||
Project: *project,
|
||||
|
||||
@@ -2,11 +2,11 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
@@ -89,18 +89,19 @@ func (t *RetryEnrichmentTool) Handle(ctx context.Context, req *mcp.CallToolReque
|
||||
return t.retryer.Handle(ctx, req, in)
|
||||
}
|
||||
|
||||
func (r *EnrichmentRetryer) QueueThought(id uuid.UUID) {
|
||||
func (r *EnrichmentRetryer) QueueThought(id int64) {
|
||||
go func() {
|
||||
started := time.Now()
|
||||
idStr := fmt.Sprint(id)
|
||||
r.logger.Info("background metadata started",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
)
|
||||
updated, err := r.retryOne(r.backgroundCtx, id)
|
||||
if err != nil {
|
||||
r.logger.Warn("background metadata error",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
slog.Duration("duration", time.Since(started)),
|
||||
@@ -109,7 +110,7 @@ func (r *EnrichmentRetryer) QueueThought(id uuid.UUID) {
|
||||
return
|
||||
}
|
||||
r.logger.Info("background metadata complete",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
slog.Bool("updated", updated),
|
||||
@@ -129,9 +130,9 @@ func (r *EnrichmentRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest
|
||||
return nil, RetryEnrichmentOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
thoughts, err := r.store.ListThoughtsPendingMetadataRetry(ctx, limit, projectID, in.IncludeArchived, in.OlderThanDays)
|
||||
@@ -168,7 +169,7 @@ func (r *EnrichmentRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest
|
||||
updated, err := r.retryOne(ctx, thought.ID)
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
out.Failures = append(out.Failures, RetryEnrichmentFailure{ID: thought.ID.String(), Error: err.Error()})
|
||||
out.Failures = append(out.Failures, RetryEnrichmentFailure{ID: fmt.Sprint(thought.ID), Error: err.Error()})
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -191,8 +192,8 @@ func (r *EnrichmentRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest
|
||||
return nil, out, nil
|
||||
}
|
||||
|
||||
func (r *EnrichmentRetryer) retryOne(ctx context.Context, id uuid.UUID) (bool, error) {
|
||||
thought, err := r.store.GetThought(ctx, id)
|
||||
func (r *EnrichmentRetryer) retryOne(ctx context.Context, id int64) (bool, error) {
|
||||
thought, err := r.store.GetThoughtByID(ctx, id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"os"
|
||||
"path/filepath"
|
||||
@@ -148,7 +149,7 @@ func (t *FilesTool) Upload(ctx context.Context, req *mcp.CallToolRequest, in Upl
|
||||
return nil, UploadFileOutput{}, err
|
||||
}
|
||||
|
||||
uri := fileURIPrefix + out.File.ID.String()
|
||||
uri := fileURIPrefix + fmt.Sprint(out.File.ID)
|
||||
return nil, UploadFileOutput{File: out.File, URI: uri}, nil
|
||||
}
|
||||
|
||||
@@ -247,7 +248,7 @@ func (t *FilesTool) Load(ctx context.Context, _ *mcp.CallToolRequest, in LoadFil
|
||||
return nil, LoadFileOutput{}, err
|
||||
}
|
||||
|
||||
uri := fileURIPrefix + file.ID.String()
|
||||
uri := fileURIPrefix + fmt.Sprint(file.ID)
|
||||
result := &mcp.CallToolResult{
|
||||
Content: []mcp.Content{
|
||||
&mcp.EmbeddedResource{
|
||||
@@ -294,7 +295,7 @@ func (t *FilesTool) List(ctx context.Context, req *mcp.CallToolRequest, in ListF
|
||||
return nil, ListFilesOutput{}, err
|
||||
}
|
||||
|
||||
var thoughtID *uuid.UUID
|
||||
var thoughtID *int64
|
||||
if rawThoughtID := strings.TrimSpace(in.ThoughtID); rawThoughtID != "" {
|
||||
parsedThoughtID, err := parseUUID(rawThoughtID)
|
||||
if err != nil {
|
||||
@@ -304,12 +305,12 @@ func (t *FilesTool) List(ctx context.Context, req *mcp.CallToolRequest, in ListF
|
||||
if err != nil {
|
||||
return nil, ListFilesOutput{}, err
|
||||
}
|
||||
thoughtID = &parsedThoughtID
|
||||
if project != nil && thought.ProjectID != nil && *thought.ProjectID != project.ID {
|
||||
thoughtID = &thought.ID
|
||||
if project != nil && thought.ProjectID != nil && *thought.ProjectID != project.NumericID {
|
||||
return nil, ListFilesOutput{}, errInvalidInput("project does not match the linked thought's project")
|
||||
}
|
||||
if project == nil && thought.ProjectID != nil {
|
||||
project = &thoughttypes.Project{ID: *thought.ProjectID}
|
||||
project = &thoughttypes.Project{NumericID: *thought.ProjectID}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -323,7 +324,7 @@ func (t *FilesTool) List(ctx context.Context, req *mcp.CallToolRequest, in ListF
|
||||
return nil, ListFilesOutput{}, err
|
||||
}
|
||||
if project != nil {
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
return nil, ListFilesOutput{Files: files}, nil
|
||||
@@ -343,7 +344,7 @@ func (t *FilesTool) SaveDecoded(ctx context.Context, req *mcp.CallToolRequest, i
|
||||
return SaveFileOutput{}, err
|
||||
}
|
||||
|
||||
var thoughtID *uuid.UUID
|
||||
var thoughtNumericID *int64
|
||||
var projectID = projectIDPtr(project)
|
||||
if rawThoughtID := strings.TrimSpace(in.ThoughtID); rawThoughtID != "" {
|
||||
parsedThoughtID, err := parseUUID(rawThoughtID)
|
||||
@@ -354,9 +355,9 @@ func (t *FilesTool) SaveDecoded(ctx context.Context, req *mcp.CallToolRequest, i
|
||||
if err != nil {
|
||||
return SaveFileOutput{}, err
|
||||
}
|
||||
thoughtID = &parsedThoughtID
|
||||
thoughtNumericID = &thought.ID
|
||||
projectID = thought.ProjectID
|
||||
if project != nil && thought.ProjectID != nil && *thought.ProjectID != project.ID {
|
||||
if project != nil && thought.ProjectID != nil && *thought.ProjectID != project.NumericID {
|
||||
return SaveFileOutput{}, errInvalidInput("project does not match the linked thought's project")
|
||||
}
|
||||
}
|
||||
@@ -374,9 +375,7 @@ func (t *FilesTool) SaveDecoded(ctx context.Context, req *mcp.CallToolRequest, i
|
||||
SHA256: hex.EncodeToString(sum[:]),
|
||||
Content: in.Content,
|
||||
ProjectID: projectID,
|
||||
}
|
||||
if thoughtID != nil {
|
||||
file.ThoughtID = thoughtID
|
||||
ThoughtID: thoughtNumericID,
|
||||
}
|
||||
|
||||
created, err := t.store.InsertStoredFile(ctx, file)
|
||||
@@ -398,7 +397,7 @@ func (t *FilesTool) SaveDecoded(ctx context.Context, req *mcp.CallToolRequest, i
|
||||
|
||||
func thoughtAttachmentFromFile(file thoughttypes.StoredFile) thoughttypes.ThoughtAttachment {
|
||||
return thoughttypes.ThoughtAttachment{
|
||||
FileID: file.ID,
|
||||
FileID: file.GUID,
|
||||
Name: file.Name,
|
||||
MediaType: file.MediaType,
|
||||
Kind: file.Kind,
|
||||
@@ -498,11 +497,11 @@ func normalizeFileKind(explicit string, mediaType string) string {
|
||||
}
|
||||
}
|
||||
|
||||
func projectIDPtr(project *thoughttypes.Project) *uuid.UUID {
|
||||
func projectIDPtr(project *thoughttypes.Project) *int64 {
|
||||
if project == nil {
|
||||
return nil
|
||||
}
|
||||
return &project.ID
|
||||
return &project.NumericID
|
||||
}
|
||||
|
||||
func normalizeFileLimit(limit int) int {
|
||||
|
||||
@@ -3,6 +3,7 @@ package tools
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
@@ -34,6 +35,14 @@ func parseUUID(id string) (uuid.UUID, error) {
|
||||
return parsed, nil
|
||||
}
|
||||
|
||||
func parseID(id string) (int64, error) {
|
||||
n, err := strconv.ParseInt(strings.TrimSpace(id), 10, 64)
|
||||
if err != nil {
|
||||
return 0, fmt.Errorf("invalid numeric id %q", id)
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func sessionID(req *mcp.CallToolRequest) (string, error) {
|
||||
if req == nil || req.Session == nil || req.Session.ID() == "" {
|
||||
return "", newMCPError(
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/config"
|
||||
@@ -31,11 +30,11 @@ type AddLearningInput struct {
|
||||
SourceType string `json:"source_type,omitempty"`
|
||||
SourceRef string `json:"source_ref,omitempty"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id; falls back to active session project"`
|
||||
RelatedThoughtID *uuid.UUID `json:"related_thought_id,omitempty"`
|
||||
RelatedSkillID *uuid.UUID `json:"related_skill_id,omitempty"`
|
||||
ReviewedBy *string `json:"reviewed_by,omitempty"`
|
||||
DuplicateOfLearningID *uuid.UUID `json:"duplicate_of_learning_id,omitempty"`
|
||||
SupersedesLearningID *uuid.UUID `json:"supersedes_learning_id,omitempty"`
|
||||
RelatedThoughtID *int64 `json:"related_thought_id,omitempty"`
|
||||
RelatedSkillID *int64 `json:"related_skill_id,omitempty"`
|
||||
ReviewedBy *string `json:"reviewed_by,omitempty"`
|
||||
DuplicateOfLearningID *int64 `json:"duplicate_of_learning_id,omitempty"`
|
||||
SupersedesLearningID *int64 `json:"supersedes_learning_id,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
@@ -44,7 +43,7 @@ type AddLearningOutput struct {
|
||||
}
|
||||
|
||||
type GetLearningInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"learning id"`
|
||||
ID int64 `json:"id" jsonschema:"learning id"`
|
||||
}
|
||||
|
||||
type GetLearningOutput struct {
|
||||
@@ -105,7 +104,7 @@ func (t *LearningsTool) Add(ctx context.Context, req *mcp.CallToolRequest, in Ad
|
||||
learning.ActionRequired = *in.ActionRequired
|
||||
}
|
||||
if project != nil {
|
||||
learning.ProjectID = &project.ID
|
||||
learning.ProjectID = &project.NumericID
|
||||
}
|
||||
|
||||
created, err := t.store.CreateLearning(ctx, learning)
|
||||
@@ -147,7 +146,7 @@ func (t *LearningsTool) List(ctx context.Context, req *mcp.CallToolRequest, in L
|
||||
Query: strings.TrimSpace(in.Query),
|
||||
}
|
||||
if project != nil {
|
||||
filter.ProjectID = &project.ID
|
||||
filter.ProjectID = &project.NumericID
|
||||
}
|
||||
|
||||
items, err := t.store.ListLearnings(ctx, filter)
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"testing"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/mcperrors"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
func TestLearningsAddRequiresSummary(t *testing.T) {
|
||||
@@ -37,7 +36,7 @@ func TestLearningsMethodsRequireConfiguredStore(t *testing.T) {
|
||||
})
|
||||
|
||||
t.Run("get", func(t *testing.T) {
|
||||
_, _, err := tool.Get(context.Background(), nil, GetLearningInput{ID: uuid.New()})
|
||||
_, _, err := tool.Get(context.Background(), nil, GetLearningInput{ID: 0})
|
||||
if err == nil {
|
||||
t.Fatal("Get() error = nil, want error")
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
@@ -97,9 +98,9 @@ func (t *LinksTool) Related(ctx context.Context, _ *mcp.CallToolRequest, in Rela
|
||||
}
|
||||
|
||||
related := make([]RelatedThought, 0, len(linked)+t.search.DefaultLimit)
|
||||
seen := map[string]struct{}{thought.ID.String(): {}}
|
||||
seen := map[string]struct{}{fmt.Sprint(thought.ID): {}}
|
||||
for _, item := range linked {
|
||||
key := item.Thought.ID.String()
|
||||
key := fmt.Sprint(item.Thought.ID)
|
||||
seen[key] = struct{}{}
|
||||
related = append(related, RelatedThought{
|
||||
ID: key,
|
||||
@@ -117,12 +118,12 @@ func (t *LinksTool) Related(ctx context.Context, _ *mcp.CallToolRequest, in Rela
|
||||
}
|
||||
|
||||
if includeSemantic {
|
||||
semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, thought.Content, t.search.DefaultLimit, t.search.DefaultThreshold, thought.ProjectID, &thought.ID)
|
||||
semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, thought.Content, t.search.DefaultLimit, t.search.DefaultThreshold, thought.ProjectID, &thought.GUID)
|
||||
if err != nil {
|
||||
return nil, RelatedOutput{}, err
|
||||
}
|
||||
for _, item := range semantic {
|
||||
key := item.ID.String()
|
||||
key := fmt.Sprint(item.ID)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/config"
|
||||
@@ -43,9 +42,9 @@ func (t *ListTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in List
|
||||
return nil, ListOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
thoughts, err := t.store.ListThoughts(ctx, thoughttypes.ListFilter{
|
||||
@@ -61,7 +60,7 @@ func (t *ListTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in List
|
||||
return nil, ListOutput{}, err
|
||||
}
|
||||
if project != nil {
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
return nil, ListOutput{Thoughts: thoughts}, nil
|
||||
|
||||
@@ -2,11 +2,11 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
@@ -37,18 +37,18 @@ type RetryMetadataTool struct {
|
||||
|
||||
type RetryLocker struct {
|
||||
mu sync.Mutex
|
||||
locks map[uuid.UUID]time.Time
|
||||
locks map[int64]time.Time
|
||||
}
|
||||
|
||||
func NewRetryLocker() *RetryLocker {
|
||||
return &RetryLocker{locks: map[uuid.UUID]time.Time{}}
|
||||
return &RetryLocker{locks: map[int64]time.Time{}}
|
||||
}
|
||||
|
||||
func (l *RetryLocker) Acquire(id uuid.UUID, ttl time.Duration) bool {
|
||||
func (l *RetryLocker) Acquire(id int64, ttl time.Duration) bool {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.locks == nil {
|
||||
l.locks = map[uuid.UUID]time.Time{}
|
||||
l.locks = map[int64]time.Time{}
|
||||
}
|
||||
now := time.Now()
|
||||
if exp, ok := l.locks[id]; ok && exp.After(now) {
|
||||
@@ -58,7 +58,7 @@ func (l *RetryLocker) Acquire(id uuid.UUID, ttl time.Duration) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func (l *RetryLocker) Release(id uuid.UUID) {
|
||||
func (l *RetryLocker) Release(id int64) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
delete(l.locks, id)
|
||||
@@ -111,23 +111,24 @@ func (t *RetryMetadataTool) Handle(ctx context.Context, req *mcp.CallToolRequest
|
||||
return t.retryer.Handle(ctx, req, in)
|
||||
}
|
||||
|
||||
func (r *MetadataRetryer) QueueThought(id uuid.UUID) {
|
||||
func (r *MetadataRetryer) QueueThought(id int64) {
|
||||
go func() {
|
||||
started := time.Now()
|
||||
idStr := fmt.Sprint(id)
|
||||
if !r.lock.Acquire(id, 15*time.Minute) {
|
||||
return
|
||||
}
|
||||
defer r.lock.Release(id)
|
||||
|
||||
r.logger.Info("background metadata started",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
)
|
||||
updated, err := r.retryOne(r.backgroundCtx, id)
|
||||
if err != nil {
|
||||
r.logger.Warn("background metadata error",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
slog.Duration("duration", time.Since(started)),
|
||||
@@ -136,7 +137,7 @@ func (r *MetadataRetryer) QueueThought(id uuid.UUID) {
|
||||
return
|
||||
}
|
||||
r.logger.Info("background metadata complete",
|
||||
slog.String("thought_id", id.String()),
|
||||
slog.String("thought_id", idStr),
|
||||
slog.String("provider", r.metadata.PrimaryProvider()),
|
||||
slog.String("model", r.metadata.PrimaryModel()),
|
||||
slog.Bool("updated", updated),
|
||||
@@ -156,9 +157,9 @@ func (r *MetadataRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest,
|
||||
return nil, RetryMetadataOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
thoughts, err := r.store.ListThoughtsPendingMetadataRetry(ctx, limit, projectID, in.IncludeArchived, in.OlderThanDays)
|
||||
@@ -205,7 +206,7 @@ func (r *MetadataRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest,
|
||||
r.lock.Release(thought.ID)
|
||||
if err != nil {
|
||||
mu.Lock()
|
||||
out.Failures = append(out.Failures, RetryMetadataFailure{ID: thought.ID.String(), Error: err.Error()})
|
||||
out.Failures = append(out.Failures, RetryMetadataFailure{ID: fmt.Sprint(thought.ID), Error: err.Error()})
|
||||
mu.Unlock()
|
||||
return
|
||||
}
|
||||
@@ -228,8 +229,8 @@ func (r *MetadataRetryer) Handle(ctx context.Context, req *mcp.CallToolRequest,
|
||||
return nil, out, nil
|
||||
}
|
||||
|
||||
func (r *MetadataRetryer) retryOne(ctx context.Context, id uuid.UUID) (bool, error) {
|
||||
thought, err := r.store.GetThought(ctx, id)
|
||||
func (r *MetadataRetryer) retryOne(ctx context.Context, id int64) (bool, error) {
|
||||
thought, err := r.store.GetThoughtByID(ctx, id)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/config"
|
||||
@@ -27,15 +26,15 @@ func NewPlansTool(db *store.DB, sessions *session.ActiveProjects, cfg config.Sea
|
||||
// --- I/O types ---
|
||||
|
||||
type CreatePlanInput struct {
|
||||
Title string `json:"title" jsonschema:"plan title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status string `json:"status,omitempty" jsonschema:"draft|active|blocked|completed|cancelled|superseded"`
|
||||
Priority string `json:"priority,omitempty" jsonschema:"low|medium|high|critical"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id; falls back to active session project"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
DueDate string `json:"due_date,omitempty" jsonschema:"RFC3339 timestamp"`
|
||||
SupersedesPlanID *uuid.UUID `json:"supersedes_plan_id,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
Title string `json:"title" jsonschema:"plan title"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status string `json:"status,omitempty" jsonschema:"draft|active|blocked|completed|cancelled|superseded"`
|
||||
Priority string `json:"priority,omitempty" jsonschema:"low|medium|high|critical"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id; falls back to active session project"`
|
||||
Owner string `json:"owner,omitempty"`
|
||||
DueDate string `json:"due_date,omitempty" jsonschema:"RFC3339 timestamp"`
|
||||
SupersedesPlanID *int64 `json:"supersedes_plan_id,omitempty"`
|
||||
Tags []string `json:"tags,omitempty"`
|
||||
}
|
||||
|
||||
type CreatePlanOutput struct {
|
||||
@@ -43,7 +42,7 @@ type CreatePlanOutput struct {
|
||||
}
|
||||
|
||||
type GetPlanInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"plan id"`
|
||||
ID int64 `json:"id" jsonschema:"plan id"`
|
||||
}
|
||||
|
||||
type GetPlanOutput struct {
|
||||
@@ -51,21 +50,21 @@ type GetPlanOutput struct {
|
||||
}
|
||||
|
||||
type UpdatePlanInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"plan id"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status string `json:"status,omitempty" jsonschema:"draft|active|blocked|completed|cancelled|superseded"`
|
||||
Priority string `json:"priority,omitempty" jsonschema:"low|medium|high|critical"`
|
||||
Owner *string `json:"owner,omitempty" jsonschema:"empty string clears the owner"`
|
||||
DueDate string `json:"due_date,omitempty" jsonschema:"RFC3339; omit to keep, 'clear' to remove"`
|
||||
ClearDueDate bool `json:"clear_due_date,omitempty"`
|
||||
CompletedAt string `json:"completed_at,omitempty" jsonschema:"RFC3339; omit to keep, 'clear' to remove"`
|
||||
ClearCompletedAt bool `json:"clear_completed_at,omitempty"`
|
||||
ReviewedBy *string `json:"reviewed_by,omitempty" jsonschema:"empty string clears the reviewer"`
|
||||
MarkReviewed bool `json:"mark_reviewed,omitempty" jsonschema:"set last_reviewed_at to now"`
|
||||
SupersedesPlanID *uuid.UUID `json:"supersedes_plan_id,omitempty"`
|
||||
ClearSupersedesPlanID bool `json:"clear_supersedes_plan_id,omitempty"`
|
||||
Tags *[]string `json:"tags,omitempty" jsonschema:"replaces all tags when provided; pass [] to clear"`
|
||||
ID int64 `json:"id" jsonschema:"plan id"`
|
||||
Title string `json:"title,omitempty"`
|
||||
Description string `json:"description,omitempty"`
|
||||
Status string `json:"status,omitempty" jsonschema:"draft|active|blocked|completed|cancelled|superseded"`
|
||||
Priority string `json:"priority,omitempty" jsonschema:"low|medium|high|critical"`
|
||||
Owner *string `json:"owner,omitempty" jsonschema:"empty string clears the owner"`
|
||||
DueDate string `json:"due_date,omitempty" jsonschema:"RFC3339; omit to keep, 'clear' to remove"`
|
||||
ClearDueDate bool `json:"clear_due_date,omitempty"`
|
||||
CompletedAt string `json:"completed_at,omitempty" jsonschema:"RFC3339; omit to keep, 'clear' to remove"`
|
||||
ClearCompletedAt bool `json:"clear_completed_at,omitempty"`
|
||||
ReviewedBy *string `json:"reviewed_by,omitempty" jsonschema:"empty string clears the reviewer"`
|
||||
MarkReviewed bool `json:"mark_reviewed,omitempty" jsonschema:"set last_reviewed_at to now"`
|
||||
SupersedesPlanID *int64 `json:"supersedes_plan_id,omitempty"`
|
||||
ClearSupersedesPlanID bool `json:"clear_supersedes_plan_id,omitempty"`
|
||||
Tags *[]string `json:"tags,omitempty" jsonschema:"replaces all tags when provided; pass [] to clear"`
|
||||
}
|
||||
|
||||
type UpdatePlanOutput struct {
|
||||
@@ -73,7 +72,7 @@ type UpdatePlanOutput struct {
|
||||
}
|
||||
|
||||
type DeletePlanInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"plan id"`
|
||||
ID int64 `json:"id" jsonschema:"plan id"`
|
||||
}
|
||||
|
||||
type DeletePlanOutput struct {
|
||||
@@ -95,13 +94,13 @@ type ListPlansOutput struct {
|
||||
}
|
||||
|
||||
type PlanDependencyInput struct {
|
||||
PlanID uuid.UUID `json:"plan_id" jsonschema:"the plan that depends on another"`
|
||||
DependsOnPlanID uuid.UUID `json:"depends_on_plan_id" jsonschema:"the plan that must complete first"`
|
||||
PlanID int64 `json:"plan_id" jsonschema:"the plan that depends on another"`
|
||||
DependsOnPlanID int64 `json:"depends_on_plan_id" jsonschema:"the plan that must complete first"`
|
||||
}
|
||||
|
||||
type PlanRelatedInput struct {
|
||||
PlanAID uuid.UUID `json:"plan_a_id"`
|
||||
PlanBID uuid.UUID `json:"plan_b_id"`
|
||||
PlanAID int64 `json:"plan_a_id"`
|
||||
PlanBID int64 `json:"plan_b_id"`
|
||||
}
|
||||
|
||||
type PlanLinkOutput struct {
|
||||
@@ -109,12 +108,12 @@ type PlanLinkOutput struct {
|
||||
}
|
||||
|
||||
type PlanSkillInput struct {
|
||||
PlanID uuid.UUID `json:"plan_id"`
|
||||
SkillID uuid.UUID `json:"skill_id"`
|
||||
PlanID int64 `json:"plan_id"`
|
||||
SkillID int64 `json:"skill_id"`
|
||||
}
|
||||
|
||||
type ListPlanSkillsInput struct {
|
||||
PlanID uuid.UUID `json:"plan_id"`
|
||||
PlanID int64 `json:"plan_id"`
|
||||
}
|
||||
|
||||
type ListPlanSkillsOutput struct {
|
||||
@@ -122,12 +121,12 @@ type ListPlanSkillsOutput struct {
|
||||
}
|
||||
|
||||
type PlanGuardrailInput struct {
|
||||
PlanID uuid.UUID `json:"plan_id"`
|
||||
GuardrailID uuid.UUID `json:"guardrail_id"`
|
||||
PlanID int64 `json:"plan_id"`
|
||||
GuardrailID int64 `json:"guardrail_id"`
|
||||
}
|
||||
|
||||
type ListPlanGuardrailsInput struct {
|
||||
PlanID uuid.UUID `json:"plan_id"`
|
||||
PlanID int64 `json:"plan_id"`
|
||||
}
|
||||
|
||||
type ListPlanGuardrailsOutput struct {
|
||||
@@ -157,7 +156,7 @@ func (t *PlansTool) Create(ctx context.Context, req *mcp.CallToolRequest, in Cre
|
||||
Tags: normalizeStringSlice(in.Tags),
|
||||
}
|
||||
if project != nil {
|
||||
plan.ProjectID = &project.ID
|
||||
plan.ProjectID = &project.NumericID
|
||||
}
|
||||
if v := strings.TrimSpace(in.DueDate); v != "" {
|
||||
t, err := time.Parse(time.RFC3339, v)
|
||||
@@ -249,7 +248,7 @@ func (t *PlansTool) List(ctx context.Context, req *mcp.CallToolRequest, in ListP
|
||||
Query: strings.TrimSpace(in.Query),
|
||||
}
|
||||
if project != nil {
|
||||
filter.ProjectID = &project.ID
|
||||
filter.ProjectID = &project.NumericID
|
||||
}
|
||||
|
||||
plans, err := t.store.ListPlans(ctx, filter)
|
||||
|
||||
@@ -79,7 +79,7 @@ func (t *ProjectsTool) SetActive(ctx context.Context, req *mcp.CallToolRequest,
|
||||
return nil, SetActiveProjectOutput{}, err
|
||||
}
|
||||
t.sessions.Set(sid, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
return nil, SetActiveProjectOutput{Project: *project}, nil
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,6 @@ import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/ai"
|
||||
@@ -49,9 +48,9 @@ func (t *RecallTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in Re
|
||||
|
||||
limit := normalizeLimit(in.Limit, t.search)
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
semantic, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, projectID, nil)
|
||||
@@ -66,7 +65,7 @@ func (t *RecallTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in Re
|
||||
items := make([]ContextItem, 0, limit*2)
|
||||
seen := map[string]struct{}{}
|
||||
for _, result := range semantic {
|
||||
key := result.ID.String()
|
||||
key := fmt.Sprint(result.ID)
|
||||
seen[key] = struct{}{}
|
||||
items = append(items, ContextItem{
|
||||
ID: key,
|
||||
@@ -77,7 +76,7 @@ func (t *RecallTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in Re
|
||||
})
|
||||
}
|
||||
for _, thought := range recent {
|
||||
key := thought.ID.String()
|
||||
key := fmt.Sprint(thought.ID)
|
||||
if _, ok := seen[key]; ok {
|
||||
continue
|
||||
}
|
||||
@@ -97,7 +96,7 @@ func (t *RecallTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in Re
|
||||
header := "Recalled context"
|
||||
if project != nil {
|
||||
header = fmt.Sprintf("Recalled context for %s", project.Name)
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
return nil, RecallOutput{
|
||||
|
||||
@@ -2,12 +2,12 @@ package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"reflect"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
"golang.org/x/sync/semaphore"
|
||||
|
||||
@@ -68,9 +68,9 @@ func (t *ReparseMetadataTool) Handle(ctx context.Context, req *mcp.CallToolReque
|
||||
return nil, ReparseMetadataOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
thoughts, err := t.store.ListThoughtsForMetadataReparse(ctx, limit, projectID, in.IncludeArchived, in.OlderThanDays)
|
||||
@@ -114,7 +114,7 @@ func (t *ReparseMetadataTool) Handle(ctx context.Context, req *mcp.CallToolReque
|
||||
mu.Lock()
|
||||
out.Normalized++
|
||||
mu.Unlock()
|
||||
t.logger.Warn("metadata reparse extract failed, using normalized existing metadata", slog.String("thought_id", thought.ID.String()), slog.String("error", extractErr.Error()))
|
||||
t.logger.Warn("metadata reparse extract failed, using normalized existing metadata", slog.String("thought_id", fmt.Sprint(thought.ID)), slog.String("error", extractErr.Error()))
|
||||
} else {
|
||||
normalizedTarget = metadata.MarkMetadataComplete(metadata.SanitizeExtracted(extracted), t.capture, attemptedAt)
|
||||
normalizedTarget.Attachments = thought.Metadata.Attachments
|
||||
@@ -130,11 +130,11 @@ func (t *ReparseMetadataTool) Handle(ctx context.Context, req *mcp.CallToolReque
|
||||
return
|
||||
}
|
||||
|
||||
if _, updateErr := t.store.UpdateThought(ctx, thought.ID, thought.Content, nil, "", normalizedTarget, thought.ProjectID); updateErr != nil {
|
||||
if _, updateErr := t.store.UpdateThought(ctx, thought.GUID, thought.Content, nil, "", normalizedTarget, thought.ProjectID); updateErr != nil {
|
||||
mu.Lock()
|
||||
out.Failures = append(out.Failures, ReparseMetadataFailure{ID: thought.ID.String(), Error: updateErr.Error()})
|
||||
out.Failures = append(out.Failures, ReparseMetadataFailure{ID: fmt.Sprint(thought.ID), Error: updateErr.Error()})
|
||||
mu.Unlock()
|
||||
t.logger.Warn("metadata reparse update failed", slog.String("thought_id", thought.ID.String()), slog.String("error", updateErr.Error()))
|
||||
t.logger.Warn("metadata reparse update failed", slog.String("thought_id", fmt.Sprint(thought.ID)), slog.String("error", updateErr.Error()))
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ func semanticSearch(
|
||||
query string,
|
||||
limit int,
|
||||
threshold float64,
|
||||
projectID *uuid.UUID,
|
||||
projectID *int64,
|
||||
excludeID *uuid.UUID,
|
||||
) ([]thoughttypes.SearchResult, error) {
|
||||
model := embeddings.PrimaryModel()
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/ai"
|
||||
@@ -50,10 +49,10 @@ func (t *SearchTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in Se
|
||||
return nil, SearchOutput{}, err
|
||||
}
|
||||
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
projectID = &project.NumericID
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
results, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, threshold, projectID, nil)
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/session"
|
||||
@@ -59,7 +58,7 @@ func (t *SkillsTool) AddSkill(ctx context.Context, _ *mcp.CallToolRequest, in Ad
|
||||
// remove_skill
|
||||
|
||||
type RemoveSkillInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"skill id to remove"`
|
||||
ID int64 `json:"id" jsonschema:"skill id to remove"`
|
||||
}
|
||||
|
||||
type RemoveSkillOutput struct {
|
||||
@@ -147,7 +146,7 @@ func (t *SkillsTool) AddGuardrail(ctx context.Context, _ *mcp.CallToolRequest, i
|
||||
// remove_guardrail
|
||||
|
||||
type RemoveGuardrailInput struct {
|
||||
ID uuid.UUID `json:"id" jsonschema:"guardrail id to remove"`
|
||||
ID int64 `json:"id" jsonschema:"guardrail id to remove"`
|
||||
}
|
||||
|
||||
type RemoveGuardrailOutput struct {
|
||||
@@ -186,13 +185,13 @@ func (t *SkillsTool) ListGuardrails(ctx context.Context, _ *mcp.CallToolRequest,
|
||||
// add_project_skill
|
||||
|
||||
type AddProjectSkillInput struct {
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
SkillID uuid.UUID `json:"skill_id" jsonschema:"skill id to link"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
SkillID int64 `json:"skill_id" jsonschema:"skill id to link"`
|
||||
}
|
||||
|
||||
type AddProjectSkillOutput struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
SkillID uuid.UUID `json:"skill_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
SkillID int64 `json:"skill_id"`
|
||||
}
|
||||
|
||||
func (t *SkillsTool) AddProjectSkill(ctx context.Context, req *mcp.CallToolRequest, in AddProjectSkillInput) (*mcp.CallToolResult, AddProjectSkillOutput, error) {
|
||||
@@ -200,17 +199,17 @@ func (t *SkillsTool) AddProjectSkill(ctx context.Context, req *mcp.CallToolReque
|
||||
if err != nil {
|
||||
return nil, AddProjectSkillOutput{}, err
|
||||
}
|
||||
if err := t.store.AddProjectSkill(ctx, project.ID, in.SkillID); err != nil {
|
||||
if err := t.store.AddProjectSkill(ctx, project.NumericID, in.SkillID); err != nil {
|
||||
return nil, AddProjectSkillOutput{}, err
|
||||
}
|
||||
return nil, AddProjectSkillOutput{ProjectID: project.ID, SkillID: in.SkillID}, nil
|
||||
return nil, AddProjectSkillOutput{ProjectID: project.NumericID, SkillID: in.SkillID}, nil
|
||||
}
|
||||
|
||||
// remove_project_skill
|
||||
|
||||
type RemoveProjectSkillInput struct {
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
SkillID uuid.UUID `json:"skill_id" jsonschema:"skill id to unlink"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
SkillID int64 `json:"skill_id" jsonschema:"skill id to unlink"`
|
||||
}
|
||||
|
||||
type RemoveProjectSkillOutput struct {
|
||||
@@ -222,7 +221,7 @@ func (t *SkillsTool) RemoveProjectSkill(ctx context.Context, req *mcp.CallToolRe
|
||||
if err != nil {
|
||||
return nil, RemoveProjectSkillOutput{}, err
|
||||
}
|
||||
if err := t.store.RemoveProjectSkill(ctx, project.ID, in.SkillID); err != nil {
|
||||
if err := t.store.RemoveProjectSkill(ctx, project.NumericID, in.SkillID); err != nil {
|
||||
return nil, RemoveProjectSkillOutput{}, err
|
||||
}
|
||||
return nil, RemoveProjectSkillOutput{Removed: true}, nil
|
||||
@@ -235,7 +234,7 @@ type ListProjectSkillsInput struct {
|
||||
}
|
||||
|
||||
type ListProjectSkillsOutput struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
Skills []ext.AgentSkill `json:"skills"`
|
||||
}
|
||||
|
||||
@@ -244,26 +243,26 @@ func (t *SkillsTool) ListProjectSkills(ctx context.Context, req *mcp.CallToolReq
|
||||
if err != nil {
|
||||
return nil, ListProjectSkillsOutput{}, err
|
||||
}
|
||||
skills, err := t.store.ListProjectSkills(ctx, project.ID)
|
||||
skills, err := t.store.ListProjectSkills(ctx, project.NumericID)
|
||||
if err != nil {
|
||||
return nil, ListProjectSkillsOutput{}, err
|
||||
}
|
||||
if skills == nil {
|
||||
skills = []ext.AgentSkill{}
|
||||
}
|
||||
return nil, ListProjectSkillsOutput{ProjectID: project.ID, Skills: skills}, nil
|
||||
return nil, ListProjectSkillsOutput{ProjectID: project.NumericID, Skills: skills}, nil
|
||||
}
|
||||
|
||||
// add_project_guardrail
|
||||
|
||||
type AddProjectGuardrailInput struct {
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
GuardrailID uuid.UUID `json:"guardrail_id" jsonschema:"guardrail id to link"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
GuardrailID int64 `json:"guardrail_id" jsonschema:"guardrail id to link"`
|
||||
}
|
||||
|
||||
type AddProjectGuardrailOutput struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
GuardrailID uuid.UUID `json:"guardrail_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
GuardrailID int64 `json:"guardrail_id"`
|
||||
}
|
||||
|
||||
func (t *SkillsTool) AddProjectGuardrail(ctx context.Context, req *mcp.CallToolRequest, in AddProjectGuardrailInput) (*mcp.CallToolResult, AddProjectGuardrailOutput, error) {
|
||||
@@ -271,17 +270,17 @@ func (t *SkillsTool) AddProjectGuardrail(ctx context.Context, req *mcp.CallToolR
|
||||
if err != nil {
|
||||
return nil, AddProjectGuardrailOutput{}, err
|
||||
}
|
||||
if err := t.store.AddProjectGuardrail(ctx, project.ID, in.GuardrailID); err != nil {
|
||||
if err := t.store.AddProjectGuardrail(ctx, project.NumericID, in.GuardrailID); err != nil {
|
||||
return nil, AddProjectGuardrailOutput{}, err
|
||||
}
|
||||
return nil, AddProjectGuardrailOutput{ProjectID: project.ID, GuardrailID: in.GuardrailID}, nil
|
||||
return nil, AddProjectGuardrailOutput{ProjectID: project.NumericID, GuardrailID: in.GuardrailID}, nil
|
||||
}
|
||||
|
||||
// remove_project_guardrail
|
||||
|
||||
type RemoveProjectGuardrailInput struct {
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
GuardrailID uuid.UUID `json:"guardrail_id" jsonschema:"guardrail id to unlink"`
|
||||
Project string `json:"project,omitempty" jsonschema:"project name or id (uses active project if omitted)"`
|
||||
GuardrailID int64 `json:"guardrail_id" jsonschema:"guardrail id to unlink"`
|
||||
}
|
||||
|
||||
type RemoveProjectGuardrailOutput struct {
|
||||
@@ -293,7 +292,7 @@ func (t *SkillsTool) RemoveProjectGuardrail(ctx context.Context, req *mcp.CallTo
|
||||
if err != nil {
|
||||
return nil, RemoveProjectGuardrailOutput{}, err
|
||||
}
|
||||
if err := t.store.RemoveProjectGuardrail(ctx, project.ID, in.GuardrailID); err != nil {
|
||||
if err := t.store.RemoveProjectGuardrail(ctx, project.NumericID, in.GuardrailID); err != nil {
|
||||
return nil, RemoveProjectGuardrailOutput{}, err
|
||||
}
|
||||
return nil, RemoveProjectGuardrailOutput{Removed: true}, nil
|
||||
@@ -306,7 +305,7 @@ type ListProjectGuardrailsInput struct {
|
||||
}
|
||||
|
||||
type ListProjectGuardrailsOutput struct {
|
||||
ProjectID uuid.UUID `json:"project_id"`
|
||||
ProjectID int64 `json:"project_id"`
|
||||
Guardrails []ext.AgentGuardrail `json:"guardrails"`
|
||||
}
|
||||
|
||||
@@ -315,12 +314,12 @@ func (t *SkillsTool) ListProjectGuardrails(ctx context.Context, req *mcp.CallToo
|
||||
if err != nil {
|
||||
return nil, ListProjectGuardrailsOutput{}, err
|
||||
}
|
||||
guardrails, err := t.store.ListProjectGuardrails(ctx, project.ID)
|
||||
guardrails, err := t.store.ListProjectGuardrails(ctx, project.NumericID)
|
||||
if err != nil {
|
||||
return nil, ListProjectGuardrailsOutput{}, err
|
||||
}
|
||||
if guardrails == nil {
|
||||
guardrails = []ext.AgentGuardrail{}
|
||||
}
|
||||
return nil, ListProjectGuardrailsOutput{ProjectID: project.ID, Guardrails: guardrails}, nil
|
||||
return nil, ListProjectGuardrailsOutput{ProjectID: project.NumericID, Guardrails: guardrails}, nil
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ import (
|
||||
"context"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
"github.com/modelcontextprotocol/go-sdk/mcp"
|
||||
|
||||
"git.warky.dev/wdevs/amcs/internal/ai"
|
||||
@@ -49,9 +48,9 @@ func (t *SummarizeTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in
|
||||
count := 0
|
||||
|
||||
if query != "" {
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
results, err := semanticSearch(ctx, t.store, t.embeddings, t.search, query, limit, t.search.DefaultThreshold, projectID, nil)
|
||||
if err != nil {
|
||||
@@ -62,9 +61,9 @@ func (t *SummarizeTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in
|
||||
}
|
||||
count = len(results)
|
||||
} else {
|
||||
var projectID *uuid.UUID
|
||||
var projectID *int64
|
||||
if project != nil {
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
thoughts, err := t.store.RecentThoughts(ctx, projectID, limit, in.Days)
|
||||
if err != nil {
|
||||
@@ -83,7 +82,7 @@ func (t *SummarizeTool) Handle(ctx context.Context, req *mcp.CallToolRequest, in
|
||||
return nil, SummarizeOutput{}, err
|
||||
}
|
||||
if project != nil {
|
||||
_ = t.store.TouchProject(ctx, project.ID)
|
||||
_ = t.store.TouchProject(ctx, project.NumericID)
|
||||
}
|
||||
|
||||
return nil, SummarizeOutput{Summary: summary, Count: count}, nil
|
||||
|
||||
@@ -83,7 +83,7 @@ func (t *UpdateTool) Handle(ctx context.Context, _ *mcp.CallToolRequest, in Upda
|
||||
if err != nil {
|
||||
return nil, UpdateOutput{}, err
|
||||
}
|
||||
projectID = &project.ID
|
||||
projectID = &project.NumericID
|
||||
}
|
||||
|
||||
updated, err := t.store.UpdateThought(ctx, id, content, embedding, embeddingModel, mergedMetadata, projectID)
|
||||
|
||||
Reference in New Issue
Block a user