| 1 | package memoria |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | |
| 6 | "amadan.net/oficina/memoria/internal/memoria/provider" |
| 7 | ) |
| 8 | |
| 9 | // Extractor is what §8.9 asks of a model: every postal address in the |
| 10 | // sent passages, as pointers into them. provider.Client satisfies it; so |
| 11 | // does a test's recorder, which is how gate 39 sees exactly what was sent. |
| 12 | type Extractor interface { |
| 13 | ExtractAddresses(ctx context.Context, passages []string) ([]provider.Pointer, error) |
| 14 | } |
| 15 | |
| 16 | // WireModel switches the model layer on when the owner has asked for it, |
| 17 | // and leaves it off otherwise. |
| 18 | // |
| 19 | // One call from main, with the logic here where it has tests — the |
| 20 | // WireSiblings shape, for the reason AGENTS.md gives: a gate over main's |
| 21 | // text can see that a call is spelled and never that it is reached, so |
| 22 | // main should hold nothing worth getting wrong. The wiring gate lists |
| 23 | // this call because its absence is silent: MEMORIA_AI=on would do nothing |
| 24 | // at all, and nothing would say so. |
| 25 | func WireModel(a *app, cfg Config) error { |
| 26 | if !cfg.AI { |
| 27 | return nil |
| 28 | } |
| 29 | c, err := provider.New(cfg.TensorXKey) |
| 30 | if err != nil { |
| 31 | return err |
| 32 | } |
| 33 | a.store.extractor = c |
| 34 | // Named, because decision 4 names the provider everywhere, and this |
| 35 | // is the line an operator reads to learn that content will leave. |
| 36 | // The key is not in it. |
| 37 | a.logger.Info("model layer on", "provider", provider.Name, "model", provider.Model) |
| 38 | return nil |
| 39 | } |
| 40 | |
| 41 | // UseFakeModel wires a stand-in model, for tools/drive and tests. |
| 42 | // |
| 43 | // Named for what it is, and production never calls it — |
| 44 | // TestProductionDoesNotUseTheTestConstructor fails if cmd/ does. It |
| 45 | // matters more here than for the other seams: this one switches the |
| 46 | // model layer on WITHOUT MEMORIA_AI, so in main it would bypass the |
| 47 | // owner's opt-in (decision 4) — and, handed a real client, send content |
| 48 | // the owner never agreed to send. |
| 49 | func (a *app) UseFakeModel(e Extractor) { a.store.extractor = e } |
| 50 | |
| 51 | // modelOn reports whether this instance may send passages to the model. |
| 52 | // Every page's footer states the egress claim, and the claim depends on |
| 53 | // this; one helper so no page can compute it differently. |
| 54 | func (a *app) modelOn() bool { return a.store != nil && a.store.extractor != nil } |
| 55 | |