A review of 5289cb4 found the ASR report was not honest arithmetic:
- "audio s" was the last segment's end, not the media length. The real
container durations are 88.93 / 109.18 / 60.07 s, so the report understated
the audio by 48 s and every speed cell was wrong.
- Decoding was unpinned, so a rerun was a new draw: WER moved 0.242 to 0.262
and one video moved 0.278 to 0.583. Greedy single-temperature decoding with
no carry-over now answers the same way twice, and the harness runs the pass
twice to show it.
- The report claimed its WER was an upper bound. It is not a bound in either
direction: a caption that drops words also hides words the model dropped.
The denominators (201 / 296 / 36 reference words) are printed instead.
- "66 speech atoms" was bench-side arithmetic. The store now says 80 audio
atoms carrying 563 word timestamps, read back after a real ingest.
Same silent-skip bug in a second place: a text file over max_text_file_bytes
returned an empty batch with no reason. It raises FileTooLarge with its size,
and a walk records that refusal like any other.
Also fixed from the same review: the Go search tool never published the audio
modality, cli_senses carried a second json_dump, the no-audio-track branch had
a test that fed it markdown, _seconds was an empty wrapper, and the docstring
quoted a speed no report measured.
Gate root cause: bench/tests imports the bench package by name with no root
conftest, so bare pytest could not collect it at all. pythonpath in pyproject
fixes both launch styles; 212 tests pass either way.
Verification: 212 passed (bare pytest and python -m pytest), ruff clean,
mypy clean on the six touched files with a cold cache, repo-wide mypy 24/6
unchanged from HEAD, pyright 0 errors on the touched files, go build/vet/test
and gofmt clean.
222 lines
8.6 KiB
Go
222 lines
8.6 KiB
Go
package mcpserver
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
|
|
"github.com/ZacharyZhang-NY/ely-eye/ely-eye-mcp/internal/apiclient"
|
|
"github.com/ZacharyZhang-NY/ely-eye/ely-eye-mcp/internal/contextpack"
|
|
"github.com/ZacharyZhang-NY/ely-eye/ely-eye-mcp/internal/domain"
|
|
"github.com/modelcontextprotocol/go-sdk/mcp"
|
|
)
|
|
|
|
type EmptyInput struct{}
|
|
|
|
type ListCartridgesInput struct {
|
|
Limit int `json:"limit,omitempty" jsonschema:"maximum number of cartridges to return, capped at 200"`
|
|
}
|
|
|
|
type GetCartridgeInput struct {
|
|
CartridgeID string `json:"cartridge_id,omitempty" jsonschema:"exact Ely-Eye cartridge id"`
|
|
Name string `json:"name,omitempty" jsonschema:"exact Ely-Eye cartridge name"`
|
|
}
|
|
|
|
type SearchAtomsInput struct {
|
|
Query string `json:"query" jsonschema:"search query, matched by the server's own ranking"`
|
|
Modality string `json:"modality,omitempty" jsonschema:"optional exact modality filter such as text, code, image, audio, pdf_page, pdf_region, video_frame, ui_screenshot"`
|
|
Holder string `json:"holder,omitempty" jsonschema:"optional: only this holder's memories may answer"`
|
|
Scenario string `json:"scenario,omitempty" jsonschema:"optional: only this scenario, e.g. home or work"`
|
|
Limit int `json:"limit,omitempty" jsonschema:"maximum number of atoms to return, capped at 100"`
|
|
}
|
|
|
|
type FetchAtomInput struct {
|
|
AtomID string `json:"atom_id" jsonschema:"exact Evidence Atom id"`
|
|
}
|
|
|
|
type CompileContextInput struct {
|
|
Question string `json:"question" jsonschema:"question to compile into an Ely-Eye evidence pack"`
|
|
Profile string `json:"profile,omitempty" jsonschema:"live_demo, extreme_context, library_100m, or research_theater"`
|
|
Limit int `json:"limit,omitempty" jsonschema:"maximum retrieval hits to pack"`
|
|
TokenBudget int64 `json:"token_budget,omitempty" jsonschema:"explicit token budget for the evidence pack"`
|
|
}
|
|
|
|
type ListProofSuitesInput struct {
|
|
Limit int `json:"limit,omitempty" jsonschema:"maximum proof suites to return, capped at 100"`
|
|
IncludePayload bool `json:"include_payload,omitempty" jsonschema:"include full proof_suite.json payloads"`
|
|
}
|
|
|
|
type ListHashHopProofsInput struct {
|
|
Kind string `json:"kind,omitempty" jsonschema:"optional filter: text_hashhop or visual_hashhop"`
|
|
Limit int `json:"limit,omitempty" jsonschema:"maximum example proofs to include, capped at 200"`
|
|
}
|
|
|
|
type GetProofSuiteInput struct {
|
|
ProofID string `json:"proof_id" jsonschema:"exact proof suite id, for example prd_proof_suite_6bc3fa744c60a0e6"`
|
|
}
|
|
|
|
type StatusOutput struct {
|
|
Status domain.Status `json:"status"`
|
|
}
|
|
|
|
type ListCartridgesOutput struct {
|
|
Cartridges []domain.Cartridge `json:"cartridges"`
|
|
}
|
|
|
|
type GetCartridgeOutput struct {
|
|
Cartridge domain.CartridgeDetail `json:"cartridge"`
|
|
}
|
|
|
|
type SearchAtomsOutput struct {
|
|
Hits []domain.AtomSearchHit `json:"hits"`
|
|
}
|
|
|
|
type FetchAtomOutput struct {
|
|
Atom domain.Atom `json:"atom"`
|
|
}
|
|
|
|
type CompileContextOutput struct {
|
|
Context domain.EvidencePack `json:"context"`
|
|
}
|
|
|
|
type ListProofSuitesOutput struct {
|
|
ProofSuites []domain.ProofSuite `json:"proof_suites"`
|
|
}
|
|
|
|
type ListHashHopProofsOutput struct {
|
|
Report domain.HashHopReport `json:"report"`
|
|
}
|
|
|
|
type GetProofSuiteOutput struct {
|
|
ProofSuite domain.ProofSuiteDetail `json:"proof_suite"`
|
|
}
|
|
|
|
func (s *Server) registerTools(server *mcp.Server) {
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "ely_eye_status",
|
|
Title: "Ely-Eye status",
|
|
Description: "Return read-only counts and cache layer status from the local Ely-Eye database.",
|
|
}, s.statusTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "list_cartridges",
|
|
Title: "List Context Cartridges",
|
|
Description: "List local Ely-Eye Context Cartridges with DNA, token-equivalent capacity, and manifest JSON.",
|
|
}, s.listCartridgesTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "get_cartridge",
|
|
Title: "Get Context Cartridge",
|
|
Description: "Read one local Ely-Eye Context Cartridge, including artifacts, memory capsule index, and asset report when present.",
|
|
}, s.getCartridgeTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "search_atoms",
|
|
Title: "Search Evidence Atoms",
|
|
Description: "Search the local Ely-Eye FTS index and return ranked Evidence Atoms.",
|
|
}, s.searchAtomsTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "fetch_atom",
|
|
Title: "Fetch Evidence Atom",
|
|
Description: "Read one Evidence Atom by exact atom id.",
|
|
}, s.fetchAtomTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "compile_context",
|
|
Title: "Compile Evidence Context",
|
|
Description: "Compile a question into an Ely-Eye evidence pack using local retrieval and the same profile names as the app.",
|
|
}, s.compileContextTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "list_proof_suites",
|
|
Title: "List Proof Suites",
|
|
Description: "List local PRD proof suites from .ely_eye/data/eval_proofs.",
|
|
}, s.listProofSuitesTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "list_hashhop_proofs",
|
|
Title: "List HashHop Proofs",
|
|
Description: "Aggregate local HashHop and Visual HashHop long-context addressing proofs by kind, hop count, and token budget, with pass counts and example proofs.",
|
|
}, s.listHashHopProofsTool)
|
|
mcp.AddTool(server, &mcp.Tool{
|
|
Name: "get_proof_suite",
|
|
Title: "Get Proof Suite",
|
|
Description: "Read one local PRD proof suite by id, including all checks and a summary of its HashHop proofs.",
|
|
}, s.getProofSuiteTool)
|
|
}
|
|
|
|
func (s *Server) statusTool(ctx context.Context, _ *mcp.CallToolRequest, _ EmptyInput) (*mcp.CallToolResult, StatusOutput, error) {
|
|
status, err := s.store.Status(ctx)
|
|
if err != nil {
|
|
return nil, StatusOutput{}, err
|
|
}
|
|
return nil, StatusOutput{Status: status}, nil
|
|
}
|
|
|
|
func (s *Server) listCartridgesTool(ctx context.Context, _ *mcp.CallToolRequest, input ListCartridgesInput) (*mcp.CallToolResult, ListCartridgesOutput, error) {
|
|
cartridges, err := s.store.ListCartridges(ctx, input.Limit)
|
|
if err != nil {
|
|
return nil, ListCartridgesOutput{}, err
|
|
}
|
|
return nil, ListCartridgesOutput{Cartridges: cartridges}, nil
|
|
}
|
|
|
|
func (s *Server) getCartridgeTool(ctx context.Context, _ *mcp.CallToolRequest, input GetCartridgeInput) (*mcp.CallToolResult, GetCartridgeOutput, error) {
|
|
cartridge, err := s.store.GetCartridge(ctx, input.CartridgeID, input.Name)
|
|
if err != nil {
|
|
return nil, GetCartridgeOutput{}, err
|
|
}
|
|
return nil, GetCartridgeOutput{Cartridge: cartridge}, nil
|
|
}
|
|
|
|
func (s *Server) searchAtomsTool(ctx context.Context, _ *mcp.CallToolRequest, input SearchAtomsInput) (*mcp.CallToolResult, SearchAtomsOutput, error) {
|
|
result, err := s.api.Search(ctx, apiclient.SearchOptions{
|
|
Query: input.Query,
|
|
Modality: input.Modality,
|
|
Holder: input.Holder,
|
|
Scenario: input.Scenario,
|
|
Limit: input.Limit,
|
|
})
|
|
if err != nil {
|
|
return nil, SearchAtomsOutput{}, err
|
|
}
|
|
return nil, SearchAtomsOutput{Hits: contextpack.SummarizeHits(result.Hits, 1200)}, nil
|
|
}
|
|
|
|
func (s *Server) fetchAtomTool(ctx context.Context, _ *mcp.CallToolRequest, input FetchAtomInput) (*mcp.CallToolResult, FetchAtomOutput, error) {
|
|
atom, err := s.store.FetchAtom(ctx, input.AtomID)
|
|
if err != nil {
|
|
return nil, FetchAtomOutput{}, err
|
|
}
|
|
return nil, FetchAtomOutput{Atom: atom}, nil
|
|
}
|
|
|
|
func (s *Server) compileContextTool(ctx context.Context, _ *mcp.CallToolRequest, input CompileContextInput) (*mcp.CallToolResult, CompileContextOutput, error) {
|
|
if input.Question == "" {
|
|
return nil, CompileContextOutput{}, errors.New("question is required")
|
|
}
|
|
hits, err := s.api.Search(ctx, apiclient.SearchOptions{Query: input.Question, Limit: input.Limit})
|
|
if err != nil {
|
|
return nil, CompileContextOutput{}, err
|
|
}
|
|
pack := contextpack.Build(input.Question, input.Profile, hits.Hits, input.TokenBudget)
|
|
return nil, CompileContextOutput{Context: pack}, nil
|
|
}
|
|
|
|
func (s *Server) listProofSuitesTool(ctx context.Context, _ *mcp.CallToolRequest, input ListProofSuitesInput) (*mcp.CallToolResult, ListProofSuitesOutput, error) {
|
|
proofs, err := s.store.ListProofSuites(ctx, input.Limit, input.IncludePayload)
|
|
if err != nil {
|
|
return nil, ListProofSuitesOutput{}, err
|
|
}
|
|
return nil, ListProofSuitesOutput{ProofSuites: proofs}, nil
|
|
}
|
|
|
|
func (s *Server) listHashHopProofsTool(ctx context.Context, _ *mcp.CallToolRequest, input ListHashHopProofsInput) (*mcp.CallToolResult, ListHashHopProofsOutput, error) {
|
|
report, err := s.store.ListHashHopProofs(ctx, input.Kind, input.Limit)
|
|
if err != nil {
|
|
return nil, ListHashHopProofsOutput{}, err
|
|
}
|
|
return nil, ListHashHopProofsOutput{Report: report}, nil
|
|
}
|
|
|
|
func (s *Server) getProofSuiteTool(ctx context.Context, _ *mcp.CallToolRequest, input GetProofSuiteInput) (*mcp.CallToolResult, GetProofSuiteOutput, error) {
|
|
suite, err := s.store.GetProofSuite(ctx, input.ProofID)
|
|
if err != nil {
|
|
return nil, GetProofSuiteOutput{}, err
|
|
}
|
|
return nil, GetProofSuiteOutput{ProofSuite: suite}, nil
|
|
}
|