metis-memory 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- metis_memory-0.1.0/LICENSE +94 -0
- metis_memory-0.1.0/PKG-INFO +205 -0
- metis_memory-0.1.0/README.md +167 -0
- metis_memory-0.1.0/metis/__init__.py +16 -0
- metis_memory-0.1.0/metis/api/__init__.py +6 -0
- metis_memory-0.1.0/metis/api/routes.py +167 -0
- metis_memory-0.1.0/metis/api/server.py +19 -0
- metis_memory-0.1.0/metis/audit/__init__.py +4 -0
- metis_memory-0.1.0/metis/audit/export.py +28 -0
- metis_memory-0.1.0/metis/audit/replay.py +40 -0
- metis_memory-0.1.0/metis/capture/__init__.py +12 -0
- metis_memory-0.1.0/metis/capture/confirm.py +46 -0
- metis_memory-0.1.0/metis/capture/infer.py +64 -0
- metis_memory-0.1.0/metis/capture/loop.py +191 -0
- metis_memory-0.1.0/metis/capture/observe.py +43 -0
- metis_memory-0.1.0/metis/capture/remember.py +64 -0
- metis_memory-0.1.0/metis/capture/whisper.py +73 -0
- metis_memory-0.1.0/metis/cli/__init__.py +3 -0
- metis_memory-0.1.0/metis/cli/commands/__init__.py +1 -0
- metis_memory-0.1.0/metis/cli/commands/audit.py +35 -0
- metis_memory-0.1.0/metis/cli/commands/capture.py +22 -0
- metis_memory-0.1.0/metis/cli/commands/config.py +22 -0
- metis_memory-0.1.0/metis/cli/commands/demo.py +39 -0
- metis_memory-0.1.0/metis/cli/commands/fragment.py +32 -0
- metis_memory-0.1.0/metis/cli/commands/init.py +23 -0
- metis_memory-0.1.0/metis/cli/commands/memory.py +55 -0
- metis_memory-0.1.0/metis/cli/commands/model.py +55 -0
- metis_memory-0.1.0/metis/cli/commands/retrieve.py +22 -0
- metis_memory-0.1.0/metis/cli/commands/workspace.py +15 -0
- metis_memory-0.1.0/metis/cli/main.py +52 -0
- metis_memory-0.1.0/metis/cli/state.py +98 -0
- metis_memory-0.1.0/metis/conditions/__init__.py +4 -0
- metis_memory-0.1.0/metis/conditions/context.py +46 -0
- metis_memory-0.1.0/metis/conditions/exclusions.py +25 -0
- metis_memory-0.1.0/metis/conditions/matcher.py +70 -0
- metis_memory-0.1.0/metis/consent/__init__.py +9 -0
- metis_memory-0.1.0/metis/consent/contestability.py +27 -0
- metis_memory-0.1.0/metis/consent/model.py +61 -0
- metis_memory-0.1.0/metis/consent/revocation.py +37 -0
- metis_memory-0.1.0/metis/engine.py +118 -0
- metis_memory-0.1.0/metis/fragment/__init__.py +15 -0
- metis_memory-0.1.0/metis/fragment/events.py +13 -0
- metis_memory-0.1.0/metis/fragment/model.py +152 -0
- metis_memory-0.1.0/metis/fragment/schema.py +28 -0
- metis_memory-0.1.0/metis/fragment/store.py +56 -0
- metis_memory-0.1.0/metis/governance/__init__.py +14 -0
- metis_memory-0.1.0/metis/governance/authority.py +38 -0
- metis_memory-0.1.0/metis/governance/lifecycle.py +296 -0
- metis_memory-0.1.0/metis/governance/policy.py +59 -0
- metis_memory-0.1.0/metis/governance/runtime_rules.py +14 -0
- metis_memory-0.1.0/metis/integrations/__init__.py +1 -0
- metis_memory-0.1.0/metis/integrations/chap/__init__.py +20 -0
- metis_memory-0.1.0/metis/integrations/chap/adapter.py +337 -0
- metis_memory-0.1.0/metis/integrations/chap/artefacts.py +95 -0
- metis_memory-0.1.0/metis/integrations/chap/compliance.py +79 -0
- metis_memory-0.1.0/metis/integrations/chap/participants.py +54 -0
- metis_memory-0.1.0/metis/integrations/chap/review.py +23 -0
- metis_memory-0.1.0/metis/memory/__init__.py +17 -0
- metis_memory-0.1.0/metis/memory/agent_context.py +59 -0
- metis_memory-0.1.0/metis/memory/broker.py +132 -0
- metis_memory-0.1.0/metis/memory/episodic.py +35 -0
- metis_memory-0.1.0/metis/memory/procedural.py +28 -0
- metis_memory-0.1.0/metis/memory/semantic.py +51 -0
- metis_memory-0.1.0/metis/memory/tacit.py +153 -0
- metis_memory-0.1.0/metis/models/__init__.py +20 -0
- metis_memory-0.1.0/metis/models/model_config.py +78 -0
- metis_memory-0.1.0/metis/models/ollama_client.py +149 -0
- metis_memory-0.1.0/metis/models/prompts.py +41 -0
- metis_memory-0.1.0/metis/models/structured_outputs.py +82 -0
- metis_memory-0.1.0/metis/py.typed +0 -0
- metis_memory-0.1.0/metis/resources.py +46 -0
- metis_memory-0.1.0/metis/retrieval/__init__.py +9 -0
- metis_memory-0.1.0/metis/retrieval/blocked_reasons.py +33 -0
- metis_memory-0.1.0/metis/retrieval/decision.py +40 -0
- metis_memory-0.1.0/metis/retrieval/explain.py +31 -0
- metis_memory-0.1.0/metis/retrieval/gate.py +167 -0
- metis_memory-0.1.0/metis/scenarios.py +279 -0
- metis_memory-0.1.0/metis/storage/__init__.py +4 -0
- metis_memory-0.1.0/metis/storage/jsonl_store.py +25 -0
- metis_memory-0.1.0/metis/storage/sqlite_store.py +71 -0
- metis_memory-0.1.0/metis/taxonomy/__init__.py +20 -0
- metis_memory-0.1.0/metis/taxonomy/categories.py +176 -0
- metis_memory-0.1.0/metis/taxonomy/mapping.py +40 -0
- metis_memory-0.1.0/metis/validation/__init__.py +14 -0
- metis_memory-0.1.0/metis/validation/mission_group.py +28 -0
- metis_memory-0.1.0/metis/validation/promotion.py +22 -0
- metis_memory-0.1.0/metis/validation/re_elicitation.py +17 -0
- metis_memory-0.1.0/metis/validation/rejection.py +17 -0
- metis_memory-0.1.0/metis/validation/states.py +42 -0
- metis_memory-0.1.0/metis/validation/tier1.py +27 -0
- metis_memory-0.1.0/metis/validation/tier2.py +52 -0
- metis_memory-0.1.0/metis_memory.egg-info/PKG-INFO +205 -0
- metis_memory-0.1.0/metis_memory.egg-info/SOURCES.txt +110 -0
- metis_memory-0.1.0/metis_memory.egg-info/dependency_links.txt +1 -0
- metis_memory-0.1.0/metis_memory.egg-info/entry_points.txt +2 -0
- metis_memory-0.1.0/metis_memory.egg-info/requires.txt +19 -0
- metis_memory-0.1.0/metis_memory.egg-info/top_level.txt +1 -0
- metis_memory-0.1.0/pyproject.toml +63 -0
- metis_memory-0.1.0/setup.cfg +4 -0
- metis_memory-0.1.0/tests/test_agent_context.py +25 -0
- metis_memory-0.1.0/tests/test_capture_loop.py +46 -0
- metis_memory-0.1.0/tests/test_chap_evidence.py +39 -0
- metis_memory-0.1.0/tests/test_chap_integration.py +45 -0
- metis_memory-0.1.0/tests/test_consent_revocation.py +46 -0
- metis_memory-0.1.0/tests/test_demo.py +26 -0
- metis_memory-0.1.0/tests/test_fragment_schema.py +39 -0
- metis_memory-0.1.0/tests/test_memory_broker.py +40 -0
- metis_memory-0.1.0/tests/test_model_assist_records.py +32 -0
- metis_memory-0.1.0/tests/test_ollama_client_mock.py +33 -0
- metis_memory-0.1.0/tests/test_retrieval_gate.py +97 -0
- metis_memory-0.1.0/tests/test_taxonomy.py +38 -0
- metis_memory-0.1.0/tests/test_validation_state_machine.py +63 -0
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity.
|
|
18
|
+
|
|
19
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
20
|
+
exercising permissions granted by this License.
|
|
21
|
+
|
|
22
|
+
"Source" form shall mean the preferred form for making modifications.
|
|
23
|
+
|
|
24
|
+
"Object" form shall mean any form resulting from mechanical
|
|
25
|
+
transformation or translation of a Source form.
|
|
26
|
+
|
|
27
|
+
"Work" shall mean the work of authorship made available under the
|
|
28
|
+
License, as indicated by a copyright notice that is included in or
|
|
29
|
+
attached to the work.
|
|
30
|
+
|
|
31
|
+
"Derivative Works" shall mean any work that is based on (or derived
|
|
32
|
+
from) the Work.
|
|
33
|
+
|
|
34
|
+
"Contribution" shall mean any work of authorship submitted to
|
|
35
|
+
Licensor for inclusion in the Work.
|
|
36
|
+
|
|
37
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
38
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
39
|
+
subsequently incorporated within the Work.
|
|
40
|
+
|
|
41
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
42
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
43
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
44
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
45
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
46
|
+
Work and such Derivative Works in Source or Object form.
|
|
47
|
+
|
|
48
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
49
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
50
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
51
|
+
(except as stated in this section) patent license to make, have
|
|
52
|
+
made, use, offer to sell, sell, import, and otherwise transfer the
|
|
53
|
+
Work.
|
|
54
|
+
|
|
55
|
+
4. Redistribution. You may reproduce and distribute copies of the Work
|
|
56
|
+
or Derivative Works thereof in any medium, with or without
|
|
57
|
+
modifications, provided that You meet the conditions of this License.
|
|
58
|
+
|
|
59
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
60
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
61
|
+
shall be under the terms and conditions of this License.
|
|
62
|
+
|
|
63
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
64
|
+
names, trademarks, service marks, or product names of the Licensor.
|
|
65
|
+
|
|
66
|
+
7. Disclaimer of Warranty. Unless required by applicable law or agreed
|
|
67
|
+
to in writing, Licensor provides the Work on an "AS IS" BASIS,
|
|
68
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
69
|
+
implied.
|
|
70
|
+
|
|
71
|
+
8. Limitation of Liability. In no event and under no legal theory shall
|
|
72
|
+
any Contributor be liable to You for damages arising from the use of
|
|
73
|
+
the Work.
|
|
74
|
+
|
|
75
|
+
9. Accepting Warranty or Additional Liability. You may choose to offer,
|
|
76
|
+
and charge a fee for, acceptance of support, warranty, indemnity, or
|
|
77
|
+
other liability obligations and/or rights consistent with this
|
|
78
|
+
License.
|
|
79
|
+
|
|
80
|
+
END OF TERMS AND CONDITIONS
|
|
81
|
+
|
|
82
|
+
Copyright 2026 Metis contributors
|
|
83
|
+
|
|
84
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
85
|
+
you may not use this file except in compliance with the License.
|
|
86
|
+
You may obtain a copy of the License at
|
|
87
|
+
|
|
88
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
89
|
+
|
|
90
|
+
Unless required by applicable law or agreed to in writing, software
|
|
91
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
92
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
93
|
+
See the License for the specific language governing permissions and
|
|
94
|
+
limitations under the License.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: metis-memory
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CHAP-aligned, local-first toolkit for capturing, validating, governing, and retrieving tacit fragments as governed tacit memory for AI agents.
|
|
5
|
+
Author-email: Brightbeam AI <oss@brightbeam.ai>
|
|
6
|
+
License: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/BrightbeamAI/metis
|
|
8
|
+
Project-URL: Documentation, https://github.com/BrightbeamAI/metis/tree/main/docs
|
|
9
|
+
Project-URL: CHAP protocol, https://github.com/BrightbeamAI/chap
|
|
10
|
+
Project-URL: Paper, https://www.preprints.org/manuscript/202608.0927
|
|
11
|
+
Keywords: tacit-knowledge,governance,agentic-ai,memory,chap,audit
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Science/Research
|
|
14
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
17
|
+
Requires-Python: >=3.10
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
License-File: LICENSE
|
|
20
|
+
Requires-Dist: chap-coordinator>=0.2.9
|
|
21
|
+
Requires-Dist: pydantic>=2.5
|
|
22
|
+
Requires-Dist: typer>=0.9
|
|
23
|
+
Requires-Dist: PyYAML>=6.0
|
|
24
|
+
Requires-Dist: cryptography>=41.0
|
|
25
|
+
Requires-Dist: httpx>=0.24
|
|
26
|
+
Provides-Extra: api
|
|
27
|
+
Requires-Dist: fastapi>=0.110; extra == "api"
|
|
28
|
+
Requires-Dist: uvicorn>=0.23; extra == "api"
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: pytest>=7; extra == "dev"
|
|
31
|
+
Requires-Dist: ruff>=0.4; extra == "dev"
|
|
32
|
+
Requires-Dist: jsonschema>=4; extra == "dev"
|
|
33
|
+
Requires-Dist: fastapi>=0.110; extra == "dev"
|
|
34
|
+
Requires-Dist: uvicorn>=0.23; extra == "dev"
|
|
35
|
+
Requires-Dist: build>=1; extra == "dev"
|
|
36
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
37
|
+
Dynamic: license-file
|
|
38
|
+
|
|
39
|
+
<p align="center">
|
|
40
|
+
<img src="docs/assets/brightbeam-logo.png" alt="Brightbeam" width="210">
|
|
41
|
+
</p>
|
|
42
|
+
|
|
43
|
+
<h1 align="center">Metis: Governed Tacit Memory for AI Agents</h1>
|
|
44
|
+
|
|
45
|
+
<p align="center"><b>Capture how expert work actually gets done, govern it, and serve it to AI agents as memory they are allowed to use.</b></p>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-1f6feb">
|
|
49
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-2ea043">
|
|
50
|
+
<img src="https://img.shields.io/badge/tests-passing-2ea043">
|
|
51
|
+
<img src="https://img.shields.io/badge/local--first-no%20cloud%20APIs-5A5A5A">
|
|
52
|
+
<img src="https://img.shields.io/badge/built%20on-CHAP-EA4700">
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
---
|
|
56
|
+
|
|
57
|
+
AI agents now act inside real workflows, but the knowledge that makes work go right was often never
|
|
58
|
+
written down. A technician hears a pump sounds wrong before any alarm. An inspector sees a batch
|
|
59
|
+
"looks off" before the lab confirms it. Manuals do not capture this, and naively mining it from
|
|
60
|
+
workers is unsafe and easy to get wrong.
|
|
61
|
+
|
|
62
|
+
Metis is a local-first Python toolkit that captures these moments as **governed tacit
|
|
63
|
+
fragments**, has a human group validate them, and serves only the validated ones to an AI agent,
|
|
64
|
+
under the exact conditions where they hold, with a full audit trail. It implements the governed
|
|
65
|
+
tacit-memory layer from the paper *Tacit Fragments: Operationalising Tacit Knowledge as a Governed
|
|
66
|
+
Memory Layer for Agentic AI*, and it runs on [CHAP](https://github.com/BrightbeamAI/chap) so every step is recorded
|
|
67
|
+
on a hash-linked, replayable evidence chain.
|
|
68
|
+
|
|
69
|
+
<p align="center"><img src="docs/assets/capture_loop.svg" alt="The capture loop" width="100%"></p>
|
|
70
|
+
|
|
71
|
+
## What is a tacit fragment?
|
|
72
|
+
|
|
73
|
+
Most of what makes someone good at their job never reaches a document. A tacit fragment is a small,
|
|
74
|
+
structured, governed record of one such piece of practice. It is deliberately partial: it is not a
|
|
75
|
+
worker's whole expertise, and it is never treated as fact.
|
|
76
|
+
|
|
77
|
+
Every fragment carries the things that make it safe to reuse:
|
|
78
|
+
|
|
79
|
+
- **what** was observed and worker-confirmed, and its category (the K1 to K17 taxonomy of tacit knowledge),
|
|
80
|
+
- **the conditions** under which it applies (site, equipment, operating mode, shift, role, risk, and exclusions),
|
|
81
|
+
- **where it came from**: provenance, the worker, and their consent,
|
|
82
|
+
- **the evidence** behind it (recurrence, supporting cases, counterexamples),
|
|
83
|
+
- **an authority layer**: Evidence (learning only), Advisory (conditional guidance), or Controlled (formal instruction),
|
|
84
|
+
- **use constraints** that travel with it.
|
|
85
|
+
|
|
86
|
+
That structure is the point. A document chunk has no conditions, consent, or authority, and a
|
|
87
|
+
training example is treated as ground truth. A tacit fragment is neither. It is situated guidance an
|
|
88
|
+
agent may use only where it applies, and must stop using the moment it does not.
|
|
89
|
+
|
|
90
|
+
## A concrete example
|
|
91
|
+
|
|
92
|
+
A pump SOP says: reduce load only when the alarm threshold is crossed. Experienced operators reduce
|
|
93
|
+
throughput earlier, when high load coincides with low-frequency vibration and a dull acoustic cue.
|
|
94
|
+
Metis captures that gap, a human group promotes it to an advisory cue, and an agent can then use
|
|
95
|
+
it, but only on the right pump in the right state.
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
from metis import MetisEngine
|
|
99
|
+
from metis.conditions.context import TacitContext
|
|
100
|
+
from metis.consent.model import ConsentRecord, ConsentStatus
|
|
101
|
+
|
|
102
|
+
eng = MetisEngine() # local and deterministic, no cloud APIs
|
|
103
|
+
eng.join_default_participants()
|
|
104
|
+
|
|
105
|
+
# Capture what the operator does that the SOP does not say.
|
|
106
|
+
frag = eng.capture_observation(
|
|
107
|
+
{
|
|
108
|
+
"observation_id": "OBS-1",
|
|
109
|
+
"work_as_imagined": "Reduce load only when the alarm threshold is crossed.",
|
|
110
|
+
"work_as_done": "Reduce throughput earlier on low-frequency vibration and a dull acoustic cue.",
|
|
111
|
+
"context": TacitContext(equipment_family="centrifugal_pump", operating_mode="high_load"),
|
|
112
|
+
},
|
|
113
|
+
consent=ConsentRecord(consent_status=ConsentStatus.granted),
|
|
114
|
+
category="K7_sensory",
|
|
115
|
+
).fragment # lands in the Evidence layer, not yet usable
|
|
116
|
+
|
|
117
|
+
# A human Mission Group promotes it. A model never makes this call.
|
|
118
|
+
eng.tier2_review(frag.fragment_id, "promoted_to_advisory", summary="advisory cue only")
|
|
119
|
+
|
|
120
|
+
# An agent asks for guidance. The gate returns it only when the context matches.
|
|
121
|
+
match = TacitContext(equipment_family="centrifugal_pump", operating_mode="high_load", risk_class="moderate")
|
|
122
|
+
other = TacitContext(equipment_family="gear_pump", operating_mode="low_load", risk_class="moderate")
|
|
123
|
+
|
|
124
|
+
print(len(eng.retrieve(match).eligible)) # 1 (returned, with its use constraints)
|
|
125
|
+
print(eng.retrieve(other).blocked[0].reason) # conditions_do_not_match
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Change the pump, raise the risk class, or withdraw consent, and the same fragment is withheld with a
|
|
129
|
+
recorded reason. Retrieval is a governance decision, not a similarity search.
|
|
130
|
+
|
|
131
|
+
<p align="center"><img src="docs/assets/retrieval_gate.svg" alt="The condition-aware retrieval gate" width="100%"></p>
|
|
132
|
+
|
|
133
|
+
## Quickstart
|
|
134
|
+
|
|
135
|
+
No cloud and no GPU. The demo and tests run without any model using deterministic fixtures.
|
|
136
|
+
|
|
137
|
+
```bash
|
|
138
|
+
git clone https://github.com/BrightbeamAI/metis && cd metis
|
|
139
|
+
pip install -e .
|
|
140
|
+
metis demo manufacturing-pump-vibration
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
The installable package name is `metis-memory` (import `metis`, CLI `metis`). The official
|
|
144
|
+
[`chap-coordinator`](https://pypi.org/project/chap-coordinator/) dependency installs from PyPI
|
|
145
|
+
automatically.
|
|
146
|
+
|
|
147
|
+
The demo runs the whole flow locally and writes a replayable evidence chain. Inspect it with
|
|
148
|
+
`metis fragment list`, `metis memory list`, `metis retrieve --context <file>`, and
|
|
149
|
+
`metis audit read`.
|
|
150
|
+
|
|
151
|
+
Prefer to click through it? Open the **[interactive demo](docs/demo.html)**: pick a scenario, step
|
|
152
|
+
through the loop, and drive the gate yourself by editing the context and watching it allow or block.
|
|
153
|
+
For a guided tour, open the illustrated **[explainer](docs/explainer.html)**.
|
|
154
|
+
|
|
155
|
+
## How it works
|
|
156
|
+
|
|
157
|
+
**Capture loop.** Observe a work event, infer a candidate (a hypothesis, never trusted), whisper one
|
|
158
|
+
short bounded question to the worker, confirm with them (descriptive fidelity only), and remember the
|
|
159
|
+
result as an Evidence-layer fragment.
|
|
160
|
+
|
|
161
|
+
**Governance.** A human Mission Group reviews each fragment across fidelity, operational relevance,
|
|
162
|
+
normative alignment, and risk, then promotes it to Advisory or Controlled, or holds, rejects, or
|
|
163
|
+
re-elicits it. Evidence-layer fragments can never drive a decision or reach an agent. A local model
|
|
164
|
+
may draft a review summary, but it never decides.
|
|
165
|
+
|
|
166
|
+
**Memory and retrieval.** A promoted fragment becomes a governed memory object. A broker assembles an
|
|
167
|
+
agent context from procedural, semantic, episodic, and tacit memory, and tacit memory is reached only
|
|
168
|
+
through the condition-aware gate, which carries the use constraints with it.
|
|
169
|
+
|
|
170
|
+
## Learn more
|
|
171
|
+
|
|
172
|
+
- **[Interactive demo](docs/demo.html)** and **[explainer](docs/explainer.html)**: the fastest way to get it.
|
|
173
|
+
- **[Documentation](docs/README.md)**: architecture, governance, retrieval, the K1 to K17 taxonomy, agent use.
|
|
174
|
+
- **[ABOUT.md](ABOUT.md)**: repository map, the four memory stores, the CHAP relationship, and how to develop.
|
|
175
|
+
- **[CHAP](https://github.com/BrightbeamAI/chap)**: the protocol Metis runs on.
|
|
176
|
+
|
|
177
|
+
## Ethical use
|
|
178
|
+
|
|
179
|
+
Metis captures fragments of human work. Do not use it for covert worker monitoring. It records no
|
|
180
|
+
audio, video, biometrics, screenshots, or keystrokes, fragments are never treated as fact, and the
|
|
181
|
+
audit chain is append-only. Production use needs worker consultation, legal review, and domain
|
|
182
|
+
validation. Read [ETHICAL_USE.md](ETHICAL_USE.md) first.
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
187
|
+
|
|
188
|
+
## Citation
|
|
189
|
+
|
|
190
|
+
Metis is the reference implementation for the paper *Tacit Fragments: Operationalising Tacit
|
|
191
|
+
Knowledge as a Governed Memory Layer for Agentic AI*
|
|
192
|
+
([preprint](https://www.preprints.org/manuscript/202608.0927), also included in this repository as
|
|
193
|
+
[docs/tacit_fragments_preprint.pdf](docs/tacit_fragments_preprint.pdf)). If you use Metis in
|
|
194
|
+
research, please cite:
|
|
195
|
+
|
|
196
|
+
```bibtex
|
|
197
|
+
@article{shahid2026tacitfragments,
|
|
198
|
+
title = {Tacit Fragments: Operationalising Tacit Knowledge as a Governed Memory Layer for Agentic AI},
|
|
199
|
+
author = {Shahid, Arsalan and Suttie, Gordon and Black, Philip and Garz{\'o}n-Vico, Antonio},
|
|
200
|
+
journal = {Preprints},
|
|
201
|
+
year = {2026},
|
|
202
|
+
doi = {10.20944/preprints202608.0927.v1},
|
|
203
|
+
url = {https://www.preprints.org/manuscript/202608.0927}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="docs/assets/brightbeam-logo.png" alt="Brightbeam" width="210">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
<h1 align="center">Metis: Governed Tacit Memory for AI Agents</h1>
|
|
6
|
+
|
|
7
|
+
<p align="center"><b>Capture how expert work actually gets done, govern it, and serve it to AI agents as memory they are allowed to use.</b></p>
|
|
8
|
+
|
|
9
|
+
<p align="center">
|
|
10
|
+
<img src="https://img.shields.io/badge/python-3.10%2B-1f6feb">
|
|
11
|
+
<img src="https://img.shields.io/badge/license-Apache--2.0-2ea043">
|
|
12
|
+
<img src="https://img.shields.io/badge/tests-passing-2ea043">
|
|
13
|
+
<img src="https://img.shields.io/badge/local--first-no%20cloud%20APIs-5A5A5A">
|
|
14
|
+
<img src="https://img.shields.io/badge/built%20on-CHAP-EA4700">
|
|
15
|
+
</p>
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
AI agents now act inside real workflows, but the knowledge that makes work go right was often never
|
|
20
|
+
written down. A technician hears a pump sounds wrong before any alarm. An inspector sees a batch
|
|
21
|
+
"looks off" before the lab confirms it. Manuals do not capture this, and naively mining it from
|
|
22
|
+
workers is unsafe and easy to get wrong.
|
|
23
|
+
|
|
24
|
+
Metis is a local-first Python toolkit that captures these moments as **governed tacit
|
|
25
|
+
fragments**, has a human group validate them, and serves only the validated ones to an AI agent,
|
|
26
|
+
under the exact conditions where they hold, with a full audit trail. It implements the governed
|
|
27
|
+
tacit-memory layer from the paper *Tacit Fragments: Operationalising Tacit Knowledge as a Governed
|
|
28
|
+
Memory Layer for Agentic AI*, and it runs on [CHAP](https://github.com/BrightbeamAI/chap) so every step is recorded
|
|
29
|
+
on a hash-linked, replayable evidence chain.
|
|
30
|
+
|
|
31
|
+
<p align="center"><img src="docs/assets/capture_loop.svg" alt="The capture loop" width="100%"></p>
|
|
32
|
+
|
|
33
|
+
## What is a tacit fragment?
|
|
34
|
+
|
|
35
|
+
Most of what makes someone good at their job never reaches a document. A tacit fragment is a small,
|
|
36
|
+
structured, governed record of one such piece of practice. It is deliberately partial: it is not a
|
|
37
|
+
worker's whole expertise, and it is never treated as fact.
|
|
38
|
+
|
|
39
|
+
Every fragment carries the things that make it safe to reuse:
|
|
40
|
+
|
|
41
|
+
- **what** was observed and worker-confirmed, and its category (the K1 to K17 taxonomy of tacit knowledge),
|
|
42
|
+
- **the conditions** under which it applies (site, equipment, operating mode, shift, role, risk, and exclusions),
|
|
43
|
+
- **where it came from**: provenance, the worker, and their consent,
|
|
44
|
+
- **the evidence** behind it (recurrence, supporting cases, counterexamples),
|
|
45
|
+
- **an authority layer**: Evidence (learning only), Advisory (conditional guidance), or Controlled (formal instruction),
|
|
46
|
+
- **use constraints** that travel with it.
|
|
47
|
+
|
|
48
|
+
That structure is the point. A document chunk has no conditions, consent, or authority, and a
|
|
49
|
+
training example is treated as ground truth. A tacit fragment is neither. It is situated guidance an
|
|
50
|
+
agent may use only where it applies, and must stop using the moment it does not.
|
|
51
|
+
|
|
52
|
+
## A concrete example
|
|
53
|
+
|
|
54
|
+
A pump SOP says: reduce load only when the alarm threshold is crossed. Experienced operators reduce
|
|
55
|
+
throughput earlier, when high load coincides with low-frequency vibration and a dull acoustic cue.
|
|
56
|
+
Metis captures that gap, a human group promotes it to an advisory cue, and an agent can then use
|
|
57
|
+
it, but only on the right pump in the right state.
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
from metis import MetisEngine
|
|
61
|
+
from metis.conditions.context import TacitContext
|
|
62
|
+
from metis.consent.model import ConsentRecord, ConsentStatus
|
|
63
|
+
|
|
64
|
+
eng = MetisEngine() # local and deterministic, no cloud APIs
|
|
65
|
+
eng.join_default_participants()
|
|
66
|
+
|
|
67
|
+
# Capture what the operator does that the SOP does not say.
|
|
68
|
+
frag = eng.capture_observation(
|
|
69
|
+
{
|
|
70
|
+
"observation_id": "OBS-1",
|
|
71
|
+
"work_as_imagined": "Reduce load only when the alarm threshold is crossed.",
|
|
72
|
+
"work_as_done": "Reduce throughput earlier on low-frequency vibration and a dull acoustic cue.",
|
|
73
|
+
"context": TacitContext(equipment_family="centrifugal_pump", operating_mode="high_load"),
|
|
74
|
+
},
|
|
75
|
+
consent=ConsentRecord(consent_status=ConsentStatus.granted),
|
|
76
|
+
category="K7_sensory",
|
|
77
|
+
).fragment # lands in the Evidence layer, not yet usable
|
|
78
|
+
|
|
79
|
+
# A human Mission Group promotes it. A model never makes this call.
|
|
80
|
+
eng.tier2_review(frag.fragment_id, "promoted_to_advisory", summary="advisory cue only")
|
|
81
|
+
|
|
82
|
+
# An agent asks for guidance. The gate returns it only when the context matches.
|
|
83
|
+
match = TacitContext(equipment_family="centrifugal_pump", operating_mode="high_load", risk_class="moderate")
|
|
84
|
+
other = TacitContext(equipment_family="gear_pump", operating_mode="low_load", risk_class="moderate")
|
|
85
|
+
|
|
86
|
+
print(len(eng.retrieve(match).eligible)) # 1 (returned, with its use constraints)
|
|
87
|
+
print(eng.retrieve(other).blocked[0].reason) # conditions_do_not_match
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Change the pump, raise the risk class, or withdraw consent, and the same fragment is withheld with a
|
|
91
|
+
recorded reason. Retrieval is a governance decision, not a similarity search.
|
|
92
|
+
|
|
93
|
+
<p align="center"><img src="docs/assets/retrieval_gate.svg" alt="The condition-aware retrieval gate" width="100%"></p>
|
|
94
|
+
|
|
95
|
+
## Quickstart
|
|
96
|
+
|
|
97
|
+
No cloud and no GPU. The demo and tests run without any model using deterministic fixtures.
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
git clone https://github.com/BrightbeamAI/metis && cd metis
|
|
101
|
+
pip install -e .
|
|
102
|
+
metis demo manufacturing-pump-vibration
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
The installable package name is `metis-memory` (import `metis`, CLI `metis`). The official
|
|
106
|
+
[`chap-coordinator`](https://pypi.org/project/chap-coordinator/) dependency installs from PyPI
|
|
107
|
+
automatically.
|
|
108
|
+
|
|
109
|
+
The demo runs the whole flow locally and writes a replayable evidence chain. Inspect it with
|
|
110
|
+
`metis fragment list`, `metis memory list`, `metis retrieve --context <file>`, and
|
|
111
|
+
`metis audit read`.
|
|
112
|
+
|
|
113
|
+
Prefer to click through it? Open the **[interactive demo](docs/demo.html)**: pick a scenario, step
|
|
114
|
+
through the loop, and drive the gate yourself by editing the context and watching it allow or block.
|
|
115
|
+
For a guided tour, open the illustrated **[explainer](docs/explainer.html)**.
|
|
116
|
+
|
|
117
|
+
## How it works
|
|
118
|
+
|
|
119
|
+
**Capture loop.** Observe a work event, infer a candidate (a hypothesis, never trusted), whisper one
|
|
120
|
+
short bounded question to the worker, confirm with them (descriptive fidelity only), and remember the
|
|
121
|
+
result as an Evidence-layer fragment.
|
|
122
|
+
|
|
123
|
+
**Governance.** A human Mission Group reviews each fragment across fidelity, operational relevance,
|
|
124
|
+
normative alignment, and risk, then promotes it to Advisory or Controlled, or holds, rejects, or
|
|
125
|
+
re-elicits it. Evidence-layer fragments can never drive a decision or reach an agent. A local model
|
|
126
|
+
may draft a review summary, but it never decides.
|
|
127
|
+
|
|
128
|
+
**Memory and retrieval.** A promoted fragment becomes a governed memory object. A broker assembles an
|
|
129
|
+
agent context from procedural, semantic, episodic, and tacit memory, and tacit memory is reached only
|
|
130
|
+
through the condition-aware gate, which carries the use constraints with it.
|
|
131
|
+
|
|
132
|
+
## Learn more
|
|
133
|
+
|
|
134
|
+
- **[Interactive demo](docs/demo.html)** and **[explainer](docs/explainer.html)**: the fastest way to get it.
|
|
135
|
+
- **[Documentation](docs/README.md)**: architecture, governance, retrieval, the K1 to K17 taxonomy, agent use.
|
|
136
|
+
- **[ABOUT.md](ABOUT.md)**: repository map, the four memory stores, the CHAP relationship, and how to develop.
|
|
137
|
+
- **[CHAP](https://github.com/BrightbeamAI/chap)**: the protocol Metis runs on.
|
|
138
|
+
|
|
139
|
+
## Ethical use
|
|
140
|
+
|
|
141
|
+
Metis captures fragments of human work. Do not use it for covert worker monitoring. It records no
|
|
142
|
+
audio, video, biometrics, screenshots, or keystrokes, fragments are never treated as fact, and the
|
|
143
|
+
audit chain is append-only. Production use needs worker consultation, legal review, and domain
|
|
144
|
+
validation. Read [ETHICAL_USE.md](ETHICAL_USE.md) first.
|
|
145
|
+
|
|
146
|
+
## License
|
|
147
|
+
|
|
148
|
+
Apache-2.0. See [LICENSE](LICENSE).
|
|
149
|
+
|
|
150
|
+
## Citation
|
|
151
|
+
|
|
152
|
+
Metis is the reference implementation for the paper *Tacit Fragments: Operationalising Tacit
|
|
153
|
+
Knowledge as a Governed Memory Layer for Agentic AI*
|
|
154
|
+
([preprint](https://www.preprints.org/manuscript/202608.0927), also included in this repository as
|
|
155
|
+
[docs/tacit_fragments_preprint.pdf](docs/tacit_fragments_preprint.pdf)). If you use Metis in
|
|
156
|
+
research, please cite:
|
|
157
|
+
|
|
158
|
+
```bibtex
|
|
159
|
+
@article{shahid2026tacitfragments,
|
|
160
|
+
title = {Tacit Fragments: Operationalising Tacit Knowledge as a Governed Memory Layer for Agentic AI},
|
|
161
|
+
author = {Shahid, Arsalan and Suttie, Gordon and Black, Philip and Garz{\'o}n-Vico, Antonio},
|
|
162
|
+
journal = {Preprints},
|
|
163
|
+
year = {2026},
|
|
164
|
+
doi = {10.20944/preprints202608.0927.v1},
|
|
165
|
+
url = {https://www.preprints.org/manuscript/202608.0927}
|
|
166
|
+
}
|
|
167
|
+
```
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"""Metis, a reference toolkit for governed tacit fragment capture.
|
|
2
|
+
|
|
3
|
+
Metis captures situated human practice as partial, validated, context-bound fragments
|
|
4
|
+
tied to provenance, consent, authority, review state, and retrieval constraints, and turns
|
|
5
|
+
validated fragments into governed tacit memory objects that AI agents can use alongside
|
|
6
|
+
procedural, semantic, and episodic memory, exposed only through condition-aware governance
|
|
7
|
+
gates. It uses local Ollama/Gemma models for bounded assistance and CHAP as its protocol
|
|
8
|
+
foundation.
|
|
9
|
+
"""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
__version__ = "0.1.0"
|
|
13
|
+
|
|
14
|
+
from .engine import MetisEngine
|
|
15
|
+
|
|
16
|
+
__all__ = ["MetisEngine", "__version__"]
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
"""FastAPI routes over the same engine, models, and evidence logic as the CLI.
|
|
2
|
+
|
|
3
|
+
Optional component (install with the ``api`` extra). Governance remains deterministic and
|
|
4
|
+
human-reviewed; nothing here lets a model or an agent promote or authorise a fragment.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from fastapi import APIRouter, HTTPException
|
|
11
|
+
from pydantic import BaseModel
|
|
12
|
+
|
|
13
|
+
from ..conditions.context import TacitContext
|
|
14
|
+
from ..consent.model import ConsentRecord, ConsentStatus
|
|
15
|
+
from ..consent.revocation import RevocationReason
|
|
16
|
+
from ..engine import MetisEngine
|
|
17
|
+
from ..scenarios import run_manufacturing
|
|
18
|
+
|
|
19
|
+
router = APIRouter()
|
|
20
|
+
|
|
21
|
+
# A single in-memory engine, seeded with the manufacturing scenario for immediate data.
|
|
22
|
+
ENGINE = run_manufacturing().engine
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def reset_engine(engine: MetisEngine | None = None) -> None:
|
|
26
|
+
global ENGINE
|
|
27
|
+
ENGINE = engine or run_manufacturing().engine
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class CaptureRequest(BaseModel):
|
|
31
|
+
observation_id: str
|
|
32
|
+
work_as_imagined: str | None = None
|
|
33
|
+
work_as_done: str | None = None
|
|
34
|
+
text: str | None = None
|
|
35
|
+
context: dict[str, Any] = {}
|
|
36
|
+
category: str | None = None
|
|
37
|
+
response: str = "confirm"
|
|
38
|
+
corrected_content: str | None = None
|
|
39
|
+
title: str | None = None
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class ReviewRequest(BaseModel):
|
|
43
|
+
fragment_id: str
|
|
44
|
+
outcome: str
|
|
45
|
+
summary: str = ""
|
|
46
|
+
change_control: dict[str, Any] | None = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ContextRequest(BaseModel):
|
|
50
|
+
context: dict[str, Any]
|
|
51
|
+
role: str | None = None
|
|
52
|
+
task_id: str = "tsk_api_query"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
class RevokeRequest(BaseModel):
|
|
56
|
+
fragment_id: str
|
|
57
|
+
reason: str = "retired"
|
|
58
|
+
by: str = "human:reviewer@plant_a"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
@router.get("/workspace")
|
|
62
|
+
def get_workspace() -> dict[str, Any]:
|
|
63
|
+
return ENGINE.adapter.descriptor()
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
@router.get("/fragments")
|
|
67
|
+
def list_fragments() -> list[dict[str, Any]]:
|
|
68
|
+
return [f.model_dump(mode="json") for f in ENGINE.fragments.all()]
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@router.get("/fragments/{fragment_id}")
|
|
72
|
+
def get_fragment(fragment_id: str) -> dict[str, Any]:
|
|
73
|
+
frag = ENGINE.fragments.get(fragment_id)
|
|
74
|
+
if not frag:
|
|
75
|
+
raise HTTPException(404, "fragment not found")
|
|
76
|
+
return frag.model_dump(mode="json")
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
@router.get("/memory")
|
|
80
|
+
def list_memory() -> list[dict[str, Any]]:
|
|
81
|
+
return [m.model_dump(mode="json") for m in ENGINE.tacit_store.all()]
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
@router.get("/memory/{memory_id}")
|
|
85
|
+
def get_memory(memory_id: str) -> dict[str, Any]:
|
|
86
|
+
m = ENGINE.tacit_store.get(memory_id)
|
|
87
|
+
if not m:
|
|
88
|
+
raise HTTPException(404, "memory object not found")
|
|
89
|
+
return m.model_dump(mode="json")
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
@router.post("/memory/query")
|
|
93
|
+
def memory_query(req: ContextRequest) -> dict[str, Any]:
|
|
94
|
+
amc = ENGINE.agent_context(req.task_id, TacitContext.model_validate(req.context), role=req.role, emit=True)
|
|
95
|
+
return amc.model_dump(mode="json")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@router.post("/capture")
|
|
99
|
+
def capture(req: CaptureRequest) -> dict[str, Any]:
|
|
100
|
+
consent = ConsentRecord(consent_status=ConsentStatus.granted)
|
|
101
|
+
result = ENGINE.capture_observation(
|
|
102
|
+
dict(observation_id=req.observation_id, work_as_imagined=req.work_as_imagined,
|
|
103
|
+
work_as_done=req.work_as_done, text=req.text,
|
|
104
|
+
context=TacitContext.model_validate(req.context), source="api"),
|
|
105
|
+
consent=consent, response=req.response, corrected_content=req.corrected_content,
|
|
106
|
+
category=req.category, title=req.title,
|
|
107
|
+
conditions=TacitContext.model_validate(req.context))
|
|
108
|
+
return {"fragment": result.fragment.model_dump(mode="json") if result.fragment else None,
|
|
109
|
+
"task_id": result.task_id,
|
|
110
|
+
"model_assist_records": [a.assist_id for a in result.model_assist_records]}
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@router.post("/confirm")
|
|
114
|
+
def confirm(req: CaptureRequest) -> dict[str, Any]:
|
|
115
|
+
"""Confirmation is captured as part of the loop; this is an alias for /capture."""
|
|
116
|
+
return capture(req)
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
@router.post("/review")
|
|
120
|
+
def review(req: ReviewRequest) -> dict[str, Any]:
|
|
121
|
+
out = ENGINE.tier2_review(req.fragment_id, req.outcome, summary=req.summary,
|
|
122
|
+
change_control=req.change_control)
|
|
123
|
+
return {"outcome": req.outcome, "memory_id": out.get("memory").memory_id if out.get("memory") else None}
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
@router.post("/promote")
|
|
127
|
+
def promote(req: ReviewRequest) -> dict[str, Any]:
|
|
128
|
+
if req.outcome not in ("promoted_to_advisory", "promoted_to_controlled"):
|
|
129
|
+
req.outcome = "promoted_to_advisory"
|
|
130
|
+
return review(req)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
@router.post("/retrieve")
|
|
134
|
+
def retrieve(req: ContextRequest) -> dict[str, Any]:
|
|
135
|
+
decision = ENGINE.retrieve(TacitContext.model_validate(req.context), role=req.role)
|
|
136
|
+
return decision.model_dump(mode="json")
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@router.post("/revoke")
|
|
140
|
+
def revoke(req: RevokeRequest) -> dict[str, Any]:
|
|
141
|
+
art = ENGINE.governance.revoke(req.fragment_id, reason=RevocationReason(req.reason), by=req.by)
|
|
142
|
+
return {"revocation_record_artefact": art}
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@router.get("/audit")
|
|
146
|
+
def audit() -> list[dict[str, Any]]:
|
|
147
|
+
return ENGINE.adapter.evidence_records()
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
@router.post("/audit/export")
|
|
151
|
+
def audit_export(out: str = "evidence.jsonl") -> dict[str, Any]:
|
|
152
|
+
n = ENGINE.export_audit(out)
|
|
153
|
+
return {"exported": n, "path": out, "verified": ENGINE.verify().ok}
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@router.get("/model/status")
|
|
157
|
+
def model_status() -> dict[str, Any]:
|
|
158
|
+
c = ENGINE.model_client
|
|
159
|
+
return {"provider": c.config.provider, "model": c.config.name, "url": c.config.url,
|
|
160
|
+
"available": c.available()}
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@router.post("/model/run")
|
|
164
|
+
def model_run(prompt: str, purpose: str = "draft_whisper") -> dict[str, Any]:
|
|
165
|
+
res = ENGINE.model_client.run(purpose, prompt)
|
|
166
|
+
return {"used_live_model": res.used_live_model, "output": res.json(),
|
|
167
|
+
"note": "advisory draft only; not a governance decision"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"""Local FastAPI server. Run: ``uvicorn metis.api.server:app``. Optional component."""
|
|
2
|
+
from __future__ import annotations
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def create_app():
|
|
6
|
+
from fastapi import FastAPI
|
|
7
|
+
|
|
8
|
+
from .routes import router
|
|
9
|
+
|
|
10
|
+
application = FastAPI(
|
|
11
|
+
title="Metis",
|
|
12
|
+
description="Governed tacit fragment capture, local-first, CHAP-aligned. "
|
|
13
|
+
"Tacit memory is exposed only through condition-aware governance gates.",
|
|
14
|
+
version="0.1.0")
|
|
15
|
+
application.include_router(router)
|
|
16
|
+
return application
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
app = create_app()
|