Main classes
Agentic · Source · AgenticGraph · MultiGraph · Formalizer · EvidenceGraph · Agent · Endpoint
mercury.graph.evidence.Agentic(my_class, schema=None, endpoint=None, logger=None)
Bases: ABC
This is the parent of any class that is called by an Agent including the Agent itself.
Overview
This class provides the layer that connects tools with agents. It is not a protocol but a method to discover a protocol the class itself implements and validates. It takes inspiration from the Model Context Protocol (MCP) while being lighter, oriented towards classes that coexist within the same process although they can also represent remote services.
It provides a simple interface with four main methods:
metafor the object's metadata: What the class can do, what input it expects and what output it produces, what state the object is in, ...runfor the actual execution of a "query", i.e., the request is a valid dictionary created according to the meta.dry_runfor simulating the execution of a query, without actually running it. This validates the input and returns fast and descriptive feedback on errors.pilotfor piloting the object to a desired state. Typically, an Agentic object must be in a READY state (after having set up services, loaded data, etc.) before it can accept queries. The pilot() method "drives" the object to a desired state.
Parts of a Agentic object
ID
Architecturally, all Agentic descendants form a graph inside some Endpoint. Each Agentic has an ID that is composed of the IDs of
its parents, and of its own name joined by a /. Its name has the format: class_schema (schema is optional if there is only one instance
of the class). Agentics can use other Agentics within the same Endpoint, but they must require specific ids in the architecture.
When that requirement can be satisfied, the Endpoint will provide the Agentic with the tool by calling its add_tool method.
Capabilities
Think of capabilities as calling tools. Each tool has a unique function name, it expects input and returns output. That is specified
in the object's .meta. There is a key called "capabilities" which is a list of dictionaries in the format:
"name": {"description": ..., "parameters": ..., "returns": ...}. The "parameters" is a dictionary with the format: {"type": "object",
"properties": ..., "required": [-- the names of the required properties --]}. Each property is a dictionary with the format:
{"name": {"type": "...", "description": "..."}}. "returns" is a dictionary with the same format as "parameters" except it does not
have a "required" key.
State
State is managed internally and exposed in the .meta attribute as the key "state" containing an integer number. Additionally,
the number can have text descriptions in the .states attribute which is an Enum class. The name of the state can be obtained using
the method state_name(). By convention, negative numbers represent non recoverable errors, zero is the initial state, and positive
are sorted up to 100 which is the READY state. The states 1..99 represent intermediate states that are specific to each class.
Intent and piloting
Intent is a desired state for the object. Piloting is the process of taking the object to a desired state. This is done typically
using the mge cli. An Agentic that is "always ready" does not need to define its own pilot() method. The mge will pilot a complete
Endpoint and the Endpoint will pilot its Agentics. A class that overrides the pilot() method must set the state according to the
success or failure of the piloting process.
Running queries
This is done using the run() and dry_run() methods. Their arguments are identical, but their logic and return values are different.
In all cases, the dictionary defines a capability (see Agentic.run() for details.) that
must contain a valid function name in the object's capabilities.
In the case of a non-AI Agentic, the capability will be used to forward the request to the appropriate method. In the case of an Agent, the capability provides the function's description in the message list. In the case of an Endpoint, the capability is used to find who will handle the request.
The run() method executes and raises exceptions on errors. The dry_run() checks the request and the state of the object and
returns a dictionary with a status code and a description. (See the docstrings of the methods for details.)
Closing the Agentic
The agentic has a method close() that is called just once when the Endpoint is closing. The Agentic can track its own state
to know if it was modified and is informed by the Endpoint if it was locked for writing. The Endpoint is locked during the pilot
and serve phases. It that case, if the Agentic was modified, it should persist its state to disk or a database. (see the docstring
of close() for details.)
Validation and Debugging
The descendants are responsible for validating the input and returning/logging errors. Additionally, they can use the method
log_error() to provide further details via the logger.
Logger
The class can log events, errors, and other function calls and responses. The logger is optional can be used for debugging and
can be as simple as a python list. I must provide an append() method to add new events. A custom method can filter events or add
extra fields to the event.
API:
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
the ID of the Agentic, composed of the IDs of its endpoint, class, its own name and an optional schema. |
logger |
the logger to use for logging events. It must provide an |
|
endpoint |
Agentic
|
the Endpoint at the root of the architecture. |
tools |
dict
|
a dictionary of the Agentics it can use as tools, keyed by their IDs. |
states |
Enum
|
an optional Enum class that defines names for the states of an Agentic. It is used to improve readability and cli argument parsing. |
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
my_class
|
str
|
the name of the class, used to build the ID. It must ba a string of letters, numbers, and underscores. Typically, it is the name of the class in lowercase. |
required |
schema
|
str
|
an optional string to distinguish different instances of the same class. It is a schema (like a database, a graph, ontology, etc.) that the class is serving. |
None
|
endpoint
|
str
|
an optional endpoint Agentic. If not provided, the Agentic is the Endpoint itself. |
None
|
logger
|
list
|
an optional logger. If not provided, no logging will be done. |
None
|
Source code in mercury/graph/evidence/agentic.py
141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 | |
meta
property
This the object's metadata as a dictionary.
It is cached after the first call, but classes can modify the .meta dictionary directly to change the metadata.
Returns:
| Type | Description |
|---|---|
dict
|
_dry_run(request)
abstractmethod
This is the method that simulates the execution of a query. It MUST be implemented by the descendants.
The method Agentic.dry_run() is a wrapper that logs the request and response, this
method does the actual work.
Source code in mercury/graph/evidence/agentic.py
176 177 178 179 180 181 182 183 184 | |
_meta()
abstractmethod
This is the method that returns the metadata of the class. It MUST be implemented by the descendants.
Source code in mercury/graph/evidence/agentic.py
169 170 171 172 173 | |
_normalize_name(name)
staticmethod
Normalizes a name to be used as an ID.
That replaces spaces with underscores and removes any character that is not a letter, number, or underscore.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
the name to normalize. |
required |
Returns:
| Type | Description |
|---|---|
str
|
the normalized name. |
Source code in mercury/graph/evidence/agentic.py
363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 | |
_now()
staticmethod
Returns the current time as a formatted string.
Returns:
| Type | Description |
|---|---|
str
|
the current time as %Y-%m-%d %H:%M:%S. |
Source code in mercury/graph/evidence/agentic.py
382 383 384 385 386 387 388 389 390 | |
_run(request)
abstractmethod
This is the method that actually runs the query. It MUST be implemented by the descendants.
The method Agentic.run() is a wrapper that logs the request and response, this method
does the actual work.
Source code in mercury/graph/evidence/agentic.py
158 159 160 161 162 163 164 165 166 | |
add_tool(agentic)
Adds a tool (another Agentic this one can use) to the Agentic.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agentic
|
Agentic
|
the tool to add. It must be an Agentic and its endpoint must be the same as the current Agentic's. |
required |
Source code in mercury/graph/evidence/agentic.py
204 205 206 207 208 209 210 211 | |
close(endpoint_locked)
Closes the Agentic.
This method is called just once when the Endpoint is closing. The Agentic can track its own state to know if it was modified and is informed by the Endpoint if it was locked for writing. The Endpoint is locked during the pilot and serve phases. It that case, if the Agentic was modified, it should persist its state to disk or a database.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
endpoint_locked
|
bool
|
True if the Endpoint is locked for writing, False otherwise. |
required |
Source code in mercury/graph/evidence/agentic.py
329 330 331 332 333 334 335 336 337 338 339 340 | |
dry_run(request)
Simulates the execution of a query.
The request is a valid argument identical to the one expected by Agentic.run().
This method does not raise exceptions, it provides feedback. It should not just validate the request, but also anticipate the readiness of the Agentic whenever possible to prevent an AgenticRunInvalidState on run().
The return value is a
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
dict
|
the request to simulate. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
{'status': 0, 'description': 'Ok.'} dictionary. The status is 0 for success. 1 for busy, 2 for invalid request with an appropriate description. |
Source code in mercury/graph/evidence/agentic.py
283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 | |
log_error(message)
Logs an error message.
The class can use this to introduce events in the logger.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
message
|
str
|
the error message to log. |
required |
Source code in mercury/graph/evidence/agentic.py
214 215 216 217 218 219 220 221 222 223 224 225 226 227 | |
pilot(intent, just_once=False)
Pilots the object to a desired state.
In the parent class, this method returns the object as AlwaysReadyState.READY unless it is in an error (negative) state. The classes that need piloting must override it.
(See Intent and piloting for details.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
intent
|
str
|
the desired state to pilot to. It must be a valid state name in the object's meta. |
required |
just_once
|
bool
|
An optional argument to break without necessarily reaching the desired state after completing one iteration. The iteration is Agentic specific and can be anything, (E.g., a complete file chunked and processed, ...) That parameter is intended for when piloting can take hours or days and the user wants a finer control of the process. |
False
|
Returns:
| Type | Description |
|---|---|
bool
|
True if the object is in the desired state (or advanced towards it one step when just_once is True), False otherwise. |
Source code in mercury/graph/evidence/agentic.py
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | |
run(request)
Runs a query.
Expected input
The request is a valid (ChatGPT/litellm function call) that can be any of the following (from simplest to most complex):
- A pure function call {"arguments": (mandatory), "name": (mandatory), "id": optional, "type": "function" (not used)}
- A unique message {"content": (mandatory), "role": (mandatory), "id": optional, "tool_calls": optional}
- A list of messages [ ... ]
NOTES:
- When the Agentic has multiple capabilities, the request must be a function call since the capability is the function name.
- When an id is provided, it must be returned in the response. It is used to match requests and responses in history.
Expected output
This method does not provide user feedback on errors, other than raising exceptions that are descendants of AgenticRunException
(e.g., AgenticRunInvalidRequest, AgenticRunInvalidState, AgenticRunFailed). Use the dry_run() method to validate the request.
The returned value does not provide any status key, it just assumes the request was successful.
The returned value is a dictionary similar to a response.choices[0] in the OpenAI API, with these keys:
- "finish_reason": A string in {'stop', 'tool_calls', 'error'}. (Anything is an ending condition, unless its lower case form starts with 'tool' or 'error'.) Errors are just a "normal" ending that will be highlighted and the content will typically be an error message. Tool calls are a special condition that will be handled by the Endpoint. Only Agentics with a single capability can use tool calls, since the Endpoint will call them with the result in a list of messages.
- "message": A dictionary with {"content" (mandatory), "role" (optional), "id" (for tool calls), "tool_calls" (optional)}}
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
dict
|
the request to run. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
the response to the request. |
Source code in mercury/graph/evidence/agentic.py
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 | |
state_name(state)
Returns the name of a state given its integer value.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
state
|
int
|
the integer value of the state. |
required |
Returns:
| Type | Description |
|---|---|
str
|
the name of the state, or None if the state is not defined in the object's states Enum. |
Source code in mercury/graph/evidence/agentic.py
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 | |
mercury.graph.evidence.Source(schema, extra_args, endpoint=None, logger=None)
Bases: Agentic
The Source is an Agentic interface to a corpus of documents.
Overview
The Source can contain different types of documents and possibly call format conversion tools. Documents can be text or code.
The Source provides:
- A "chunking" interface to break documents into smaller pieces for easier processing.
- A hierarchy that divides a corpus into: a collection (that manages many files), a document (a file) a section (which can be nested).
- An indexing system that provides unique identifiers for each component.
- A persistence backend that possibly includes vectorization and embedding of the chunks for later retrieval.
- An Agentic interface to everything above.
Source Components
The Source uses the following components to manage file conversion, chunking, indexing and to represent the parts of a document:
SourceNode: The base class to manage the index logic of all components.SourceMaker: The root SourceNode responsible for managing a tree of markdown files.SourceFile: Each individual file as a SourceNode.SourceEntity: Each section, subsection, paragraph, table, figure, text, table cell or link in a markdown file as a SourceNode.
Known Limitations
See
- Warning
- Limitations
- The SourceNode tree (all the SourceEntity objects inside each SourceFile, all the SourceFiles inside the SourceMaker created on demand) can become very large and the Source has yet not mechanisms to limit the size of the tree. We postpone until a later release exploring how to do that to make Sources production ready. For now, Sources typically load very fast since everything is stored in files and can be re-loaded when they grow too much.
- The vector database is configured, created by the Source, and not used. This requires exposing new capabilities by the Source. It is postponed until a later release, since it is not part of the MVP.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
str
|
a schema (a unique name) to use for the Source's ID. |
required |
endpoint
|
Agentic
|
an optional Endpoint. It becomes part of the Source's ID and is available via |
None
|
logger
|
list
|
an optional logger to use for logging events. It must provide an |
None
|
extra_args
|
dict
|
the configuration for the Source. |
required |
Source code in mercury/graph/evidence/source.py
58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 | |
_capabilities()
Returns the capabilities of the Source.
Returns:
| Type | Description |
|---|---|
list
|
A list of capabilities, each represented as a dictionary with the following keys:
The value of 'function' is:
|
Source code in mercury/graph/evidence/source.py
303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 | |
_dry_run(request)
Simulates running the Source with the given request.
(See Agentic.dry_run().)
NOTE:
The Endpoint takes care of validating the request according to the capabilities exposed by the Source. It is not necessary to validate again here and the Endpoint does not forward the dry_run() request to the Source. This method is provided as a requirement of the Agentic interface, but it is only used when you use Sources directly outside of an Endpoint.
Source code in mercury/graph/evidence/source.py
116 117 118 119 120 121 122 123 124 125 126 127 128 | |
_meta()
Returns the metadata of the Source.
(See Agentic.meta().)
Source code in mercury/graph/evidence/source.py
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 | |
_run(request)
Runs the Source with the given request.
(See Agentic.run().)
Source code in mercury/graph/evidence/source.py
75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 | |
_setup_chroma_db()
Sets up the Chroma vector database for the Source.
The Chroma vector database is used to store embeddings of chunks for later retrieval. It is configured in the Source's configuration.
Returns:
| Type | Description |
|---|---|
bool
|
True if the Chroma vector database was set up successfully or is not used, False if setup failed. |
Source code in mercury/graph/evidence/source.py
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 | |
child(index)
Returns the corresponding SourceNode object following the SourceNode interface and serializes it to a dictionary.
(See SourceNode.child().)
Source code in mercury/graph/evidence/source.py
216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 | |
close(endpoint_locked)
Closes the Source and releases any resources it holds.
(See Agentic.close().)
Source code in mercury/graph/evidence/source.py
251 252 253 254 255 256 257 258 | |
get_children_idx(index=None)
Returns the children indices following the SourceNode interface.
(See SourceNode.get_children_idx().)
Source code in mercury/graph/evidence/source.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 | |
pilot(intent, just_once=False)
Pilots the Source to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/source.py
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
mercury.graph.evidence.AgenticGraph(schema, extra_args, endpoint=None, logger=None)
Bases: Agentic
AgenticGraph is the class that exposes a mercury.graph.Graph using the Agentic interface.
AgenticGraphs are typically persisted
A graph is a persisted storage of anything: the storage of nodes is a key/value store with any properties. On top of that, nodes can be connected by edges that also have properties. AgenticGraph objects are typically persisted (that is part of their configuration to store such things as hierarchical ontologies for both entities and relationships). They can read from .csv files that contain ontologies, for initialization, but do not write to .csv files to keep project management simple. Once the graph is initialized, it stores itself as a pickle file. When initializing the object, if the pickle file exists, it will be loaded rather than initializing the graph from .csv files.
IDs and hierarchical structure
The ontology is not only the place where concepts and relationships are defined to be used by the formalizer, but also the
place where the IDs of every node and edge in the EvidenceGraph live. Despite the number of entities being potentially very large and
AgenticGraphs typically living in RAM, the importance of creating concepts and maintaining the hierarchical structure dynamically from
text by Agentic decisions makes this "all in one place" design recommendable. Also, the mechanism used to structure text entities using
SourceNode is exactly the mechanism used in the hierarchy of the IDs.
How AgenticGraphs are used to build EvidenceGraphs
There a three components:
- The Ontologies are just graphs with IDs and definitions for concepts. Typically entities and relationships live in different graphs (relationships may not be used, all that is configurable).
- The Formalizer takes concepts defined in the Ontology (E.g., person, product, country, etc.) also possibly relationships (E.g., is_owned_by, is_located_in, etc.) and finds instances of those concepts in natural language text.
- The EvidenceGraph creates an aggregated graph of all that information handling contradictions, reinforcements and confidence.
What the AgenticGraph exposes via the Agentic interface
All the internals of a graph are basically understood by the Formalizer and the EvidenceGraph. The AgenticGraph is an understandable storage for concepts, entity indices and relationships and, via the Agentic interface that is what is accessible: A mechanism similar to that of a source to retrieve, via unique indices, concepts, entities and relationships.
Known Limitations
There is no "graph language" exposed via the Agentic interface that allows for arbitrary graph oriented queries. Note that Agentic objects have a mechanism to access the objects directly and will typically bypass the Agentic interface for intense computations such as crawling entire corpora. In the future, Agents, besides discovering the hierarchy tree may also have to modify it by creating or updating concepts and relationships.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
str
|
a schema (a unique name) to use for the AgenticGraph's ID. |
required |
extra_args
|
dict
|
the configuration for the AgenticGraph. See the configuration for the examples in ontologies.jsonc for reference on how to configure the AgenticGraph's persistence. |
required |
endpoint
|
Agentic
|
an optional Endpoint. It becomes part of the AgenticGraph's ID and is available via |
None
|
logger
|
list
|
an optional logger to use for logging events. It must provide an |
None
|
Source code in mercury/graph/evidence/agentic_graph.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
_add_index_to_tree(index)
Adds an index to the hierarchical tree structure. It traverses the tree rooted at self._indices and adds dictionaries
as required. The same tree contains node and edge indices, but the latter have their index prefixed by _edge_ which
places them in a dictionary under the _edge_ key.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
index
|
str
|
The index to add, represented as a string with components separated by '|'. |
required |
Source code in mercury/graph/evidence/agentic_graph.py
370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 | |
_build_indices()
Builds the indices for the AgenticGraph for the first time after it is loaded.
This builds a hierarchical tree structure of dictionaries, rooted at self._indices.
Source code in mercury/graph/evidence/agentic_graph.py
395 396 397 398 399 400 401 402 403 404 405 406 407 | |
_capabilities()
Returns the capabilities of the AgenticGraph.
Returns:
| Type | Description |
|---|---|
list
|
A list of capabilities, each represented as a dictionary with the following keys:
The value of 'function' is:
|
Source code in mercury/graph/evidence/agentic_graph.py
410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 | |
_dry_run(request)
Simulates running the AgenticGraph with the given request.
(See Agentic.dry_run().)
NOTE:
The Endpoint takes care of validating the request according to the capabilities exposed by the AgenticGraph. It is not necessary to validate again here and the Endpoint does not forward the dry_run() request to the AgenticGraph. This method is provided as a requirement of the Agentic interface, but it is only used when you use AgenticGraphs directly outside of an Endpoint.
Source code in mercury/graph/evidence/agentic_graph.py
184 185 186 187 188 189 190 191 192 193 194 195 196 | |
_meta()
Returns the metadata of the AgenticGraph.
(See Agentic.meta().)
Source code in mercury/graph/evidence/agentic_graph.py
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
_run(request)
Runs the AgenticGraph with the given request.
(See Agentic.run().)
Source code in mercury/graph/evidence/agentic_graph.py
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 | |
child(index)
Returns the corresponding SourceNode object following the SourceNode interface and serializes it to a dictionary.
(See SourceNode.child().)
Source code in mercury/graph/evidence/agentic_graph.py
307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 | |
close(endpoint_locked)
Closes the AgenticGraph, persists it to disk and releases any resources it holds.
(See Agentic.close().)
Source code in mercury/graph/evidence/agentic_graph.py
356 357 358 359 360 361 362 363 364 365 366 367 | |
get_children_idx(index=None)
Returns the children indices following the SourceNode interface.
(See SourceNode.get_children_idx().)
Source code in mercury/graph/evidence/agentic_graph.py
275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 | |
pilot(intent, just_once=False)
Pilots the AgenticGraph to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/agentic_graph.py
199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 | |
mercury.graph.evidence.MultiGraph(data=None, keys=None, nodes=None)
Bases: Graph
MultiGraph is a subclass of Graph, implemented only for AgenticGraph.
It demonstrates how to extend the original Graph class to support MultiDiGraph structures, but only supports NetworkX MultiDiGraph instead of all the technologies supported by the original Graph class. Its interactions with graph-specific functions and attributes in the original class are not guaranteed to work correctly.
Source code in mercury/graph/evidence/agentic_graph.py
32 33 | |
_calculate_edges_colnames()
This internal method is overridden to add the key column for MultiDiGraph edges.
Source code in mercury/graph/evidence/agentic_graph.py
64 65 66 67 68 69 70 71 72 | |
_from_pandas(edges, nodes, keys)
This internal method is overridden to construct a NetworkX MultiDiGraph (directed, not weighted) instead of the DiGraph() or Graph() of the original Graph class.
Source code in mercury/graph/evidence/agentic_graph.py
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | |
mercury.graph.evidence.Formalizer(schema, extra_args, endpoint=None, logger=None)
Bases: Agentic
The Formalizer is the class that takes in natural language and produces structured data in the form of subgraphs.
Overview
The Formalizer has a double role:
- While building the evidence graph from text in corpora, providing nodes and edges for the EvidenceGraph to be merged into a coherent structure.
- While querying the evidence graph in natural language, identifying nodes and edges in the text of a query verifying matches between them and corresponding nodes and edges in the evidence graph.
The level of "intelligence" of the Formalizer
The Formalizer "speaks" the language of EvidenceGraph, has both an Agentic interface (to be used as a tool by an Agent or just exposed in an Endpoint) and direct access (via the parent) to its ontologies and from the EvidenceGraph. It also abstracts the functionality of some "smart" model that does the heavy lifting.
It is smart enough to suggest potential nodes and edges or score how strongly the definition of one node matches another (making both actually the same entity), but the "intelligence" about how that becomes a final EvidenceGraph is handled by either an Agent or the EvidenceGraph itself.
This functionality is often called (SIE) Structured Information Extracting.
How GLiNER2 fits into this workflow
Unlike in the PdfToMarkdown conversion, where a lot of valid "turn-key" solutions
exist, and therefore we do not favor any specific tool and provide a simple configuration mechanism to integrate any tool, we
consider GLiNER2 as the mature, efficient, open-source, CPU-friendly solution for, at least, this early stage of this project.
Possible alternatives to GLiNER2 could be
- A completely LLM-based solution using a model that could reach similar performance without disproportionate computational cost.
- A SpaCy based solution for named entity recognition and relationship extraction.
- Something like NuExtract3 which for the moment has high GPU requirements, but works directly from PDF (similar to Unlimited-OCR for the PDF stage).
The functions used in GLiNER2 terms
- AutoExtractor (Basic Entity Extraction): Is used for node identification form text. It is provided a schema (from a selection of the Ontology or custom) and the output is provided with autogenerated temporary node identifiers that require further grounding in the EvidenceGraph.
- AutoExtractor (Structure Extraction): Is given an
extract_json()format created by the Formalizer to match the definitions of both the relationship and its elements from the Ontology. - AutoExtractor (Classification): With a classification schema provided. This is used to classify the entire text as containing the information or to determine if nodes belong to the same entity.
Capabilities exposed by the Formalizer
The Formalizer exposes three capabilities:
- Entity Extraction
- Relationship Extraction
- Classification
Known Limitations
The functionality is still very exploratory and minimalistic. Specifically, the following limitations exist:
- Confidence scoring is not implemented.
- There is not mechanism to specify "threshold"
- The key 'definition' is hardcoded, it should be included in the config to make it easier to find and modify.
- hint_edges() requires a different logic since it is forcing to return all possible edges, not just those
- is_a() is not implemented. It is still unclear how to merge entities and if text classification should play a role in it.
In general, the whole system is very early-stage, released for developer use and experimentation. The best way of formalizing entities and relationships from text is still to be determined. It requires a functioning architecture to experiment, and that is what we are to-some-extent providing with this early-stage implementation.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
str
|
a schema (a unique name) to use for the Formalizer's ID. |
required |
extra_args
|
dict
|
the configuration for the Formalizer. |
required |
endpoint
|
Agentic
|
an optional Endpoint. It becomes part of the Formalizer's ID and is available via |
None
|
logger
|
list
|
an optional logger to use for logging events. It must provide an |
None
|
Source code in mercury/graph/evidence/formalizer.py
108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
_capabilities()
Returns the capabilities of the Formalizer.
Returns:
| Type | Description |
|---|---|
list
|
A list of capabilities, each represented as a dictionary with the following keys:
The value of 'function' is:
|
Source code in mercury/graph/evidence/formalizer.py
436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | |
_dry_run(request)
Simulates running the Formalizer with the given request.
(See [`Agentic.dry_run()`][mercury.graph.evidence.Agentic.dry_run].)
NOTE:
The Endpoint takes care of validating the request according to the capabilities exposed by the Formalizer. It is not necessary to validate again here and the Endpoint does not forward the dry_run() request to the Formalizer. This method is provided as a requirement of the Agentic interface, but it is only used when you use Formalizers directly outside of an Endpoint.
Source code in mercury/graph/evidence/formalizer.py
160 161 162 163 164 165 166 167 168 169 170 171 172 | |
_meta()
Returns the metadata of the Formalizer.
(See Agentic.meta().)
Source code in mercury/graph/evidence/formalizer.py
143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
_run(request)
Runs the Formalizer with the given request.
(See Agentic.run().)
Source code in mercury/graph/evidence/formalizer.py
127 128 129 130 131 132 133 134 135 136 137 138 139 140 | |
close(endpoint_locked)
Closes the Formalizer, persists it to disk and releases any resources it holds.
(See Agentic.close().)
Source code in mercury/graph/evidence/formalizer.py
423 424 425 426 427 428 429 430 431 432 433 | |
hint_edges(arguments)
Identifies edges in a text from the ontology or some concepts in it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arguments
|
dict
|
A dictionary containing the following keys: - 'text' (str): The text from which to identify edges. - 'concepts' (list): A list of concepts to identify in the text. If empty, all concepts will be considered. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
A dictionary where keys are identified edges and values are their descriptions. |
Source code in mercury/graph/evidence/formalizer.py
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 | |
hint_nodes(arguments)
Identifies nodes in a text from the ontology or some concepts in it.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arguments
|
dict
|
A dictionary containing the following keys: - 'text' (str): The text from which to identify nodes. - 'concepts' (list): A list of concepts to identify in the text. If empty, all concepts will be considered. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
A dictionary where keys are identified nodes and values are their descriptions. |
Source code in mercury/graph/evidence/formalizer.py
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 | |
is_a(arguments)
Checks if a given concept is a subclass of another concept in the ontology.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
arguments
|
dict
|
A dictionary containing the following keys: - 'child' (str): The child concept to check. - 'parent' (str): The parent concept to check against. |
required |
Returns:
| Type | Description |
|---|---|
float
|
Returns a [0..1] score indicating the likelihood that the child is a subclass of the parent. |
Source code in mercury/graph/evidence/formalizer.py
402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
pilot(intent, just_once=False)
Pilots the Formalizer to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/formalizer.py
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 | |
mercury.graph.evidence.EvidenceGraph(schema, extra_args, endpoint=None, logger=None)
Bases: Agentic
The EvidenceGraph is the class that holds the knowledge about one or many sources in the form of a graph.
Overview
This is the core of the architecture.
It is essentially a multigraph, whose nodes and edges are strictly connected by their ids to ontologies and hold information about all the sources that contain every mention to the entities and relationships represented in it and evidence metrics associated with each piece of information.
It delegates, but ultimately controls, its own building process. It pilots itself only up to a point where all its tools are ready to be queried and previous persisted state is loaded, but piloting does not include processing all the sources available to it. That is done by ordering it to crawl from a starting source id.
It is the structured memory where the knowledge (defined and limited to by the concepts in the ontology that created the nodes and edges) is stored.
It is available for:
- Inspection and correction via deterministic (create, read, update, delete) operations
- Aggregation of more evidence as a continuous process
- Integration with upstream and downstream Agentic components
Storing the graph
The EvidenceGraph is a child of Agentic directly, not AgenticGraph. It does contain a MultiGraph to store the data just like
AgenticGraph does. There are enough differences (initialization, capabilities, much larger size) and the commonalities are mostly
contained in the class MultiGraph which both use to justify this decision.
The graph is persisted to disk using a pickle file and a configuration mechanism identical to that of AgenticGraph.
Connection to ontologies is via the Formalizer
An EvidenceGraph is connected to exactly one Formalizer. The tasks themselves the Formalizer does (id_nodes identifying nodes,
id_edges identifying edges and is_same determining identity and category membership) are treated as separate functionality to
make it possible to have them handled by an Agent or an agent that supervises another Agentic, etc.
The Formalizer must have its three ontology AgenticGraph objects (entities, relationships, known_ids) defined. And the
EvidenceGraph will modify these ontology objects as it integrates new evidence into the graph. It is highly recommended not to share
these AgenticGraph ontology objects with other EvidenceGraph instances.
Extraction of candidate entities and relationships
Extraction of candidate entities is controlled via the "id_nodes" configuration key that must provide the name of an Agentic object
usable as a tool. In this early implementation, the "id_nodes" tool is expected to be the Formalizer itself, any replacement should
provide a compatible capability.
The EvidenceGraph provides a similar key "id_edges", which must also expect a capability compatible with the corresponding capability
in the Formalizer that still requires research to provide a reliable implementation.
Merging of entities and relationships inside the EvidenceGraph
Those are two different problems that are currently handled by the same functionality in the Formalizer.
It is currently managed as a text classification with definitions that are not yet fully formalized. Expect this to change.
Evidence weighting and contradiction handling
The EvidenceGraph must compute aggregated evidence weights for both entities and relationships. This is a function of
- an authority score for each source
- a confidence score that the relationship or entity is correctly attributed to the given text.
- a confidence measure of the correct identification of the entities involved in the relationship.
aggregated over each chunk of evidence.
Building the entire EvidenceGraph from Sources
This is done by "crawling" the sources. The mechanism is similar to "reading" by a human, instead of training over the entire corpus. We intentionally, want the system be able to manage the situation where the sources are immense and "reading" everything would require an impractical amount of time and resources to be handled gracefully.
In the case of large corpora, like wikipedia, it is possible to parse them at a high level, just extracting top level titles, index those and their corresponding embeddings. The Sources provide that functionality. It is still an open problem to determine how "answering a question" can have a budget of resources allocated to read new content in the source and become more knowledgeable as a continuous process and not just as a static snapshot of a trained model.
Downstream Agentic Integration
For now, we are researching ways to enforce functionality that is not well supported in the Formalizer (GLiNER2) to be done by Agents. Agents, as currently implemented, should be flexible enough to provide functionality compatible with that of the Formalizer, although at a much higher computational cost. Expect this to evolve as the integration between Agents and the Formalizer matures.
Capabilities (Upstream Agentic Integration)
The first capability implemented is the ability to crawl a source, extracting all the text content, formalizing it and aggregating it. The full implementation of this capability requires proper entity and relationship recognition and aggregation mechanisms.
The EvidenceGraph, once it can reliably aggregate evidence, should not provide any "guessing" functionality, it must strictly behave like a database providing functionality to create, read, update, and delete entities and relationships by a supervisor to maintain quality EvidenceGraph as valuable data assets.
Querying EvidenceGraph
The EvidenceGraph is defined as much by what it does as by what it does not do: It does not answer queries in natural language. It is just a multigraph with downstream solution to gather text and formalize it into structured evidence.
To answer questions about the content of the EvidenceGraph, we use a specialized Agent. The Agent has access to: the EvidenceGraph, the Formalizer and the Sources that the EvidenceGraph has. The Agent can use the Formalizer to identify the entities and relationships in the query and relate them to the ontologies defined in the EvidenceGraph, possibly including user confirmation.
Finding the best way to query the EvidenceGraph through the Agent is an ongoing research topic.
Known Limitations
- This class lacks the merging mechanism for both entities and relationships.
- The size of the EvidenceGraph is limited by the current implementation to a NetworkX multigraph that fits in memory.
- Some mechanism that allows distributing the computation of one large EvidenceGraph across multiple machines will be necessary.
- The full implementation of the Source crawling functionality requires the missing mechanisms.
- Continuous and incomplete (as in the percentage of the source that has been crawled) knowledge acquisition is still an open challenge.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
str
|
a schema (a unique name) to use for the EvidenceGraph's ID. |
required |
extra_args
|
dict
|
the configuration for the EvidenceGraph. |
required |
endpoint
|
Agentic
|
an optional Endpoint. It becomes part of the EvidenceGraph's ID and is available via |
None
|
logger
|
list
|
an optional logger to use for logging events. It must provide an |
None
|
Source code in mercury/graph/evidence/evidence_graph.py
133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 | |
_capabilities()
Returns the capabilities of the EvidenceGraph.
Returns:
| Type | Description |
|---|---|
list
|
A list of capabilities, each represented as a dictionary with the following keys:
The value of 'function' is:
|
Source code in mercury/graph/evidence/evidence_graph.py
423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 | |
_connect_downstream()
Connects the EvidenceGraph to downstream components or systems.
This parses the configuration and locates every necessary downstream Agentic: the Formalizer, its ontologies, Agentics with capabilities to do entity extraction, etc. and the Sources.
Returns:
| Type | Description |
|---|---|
bool
|
True if the connection was successful, False otherwise. |
Source code in mercury/graph/evidence/evidence_graph.py
292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 | |
_dry_run(request)
Simulates running the EvidenceGraph with the given request.
(See [`Agentic.dry_run()`][mercury.graph.evidence.Agentic.dry_run].)
NOTE:
The Endpoint takes care of validating the request according to the capabilities exposed by the EvidenceGraph. It is not necessary to validate again here and the Endpoint does not forward the dry_run() request to the EvidenceGraph. This method is provided as a requirement of the Agentic interface, but it is only used when you use EvidenceGraphs directly outside of an Endpoint.
Source code in mercury/graph/evidence/evidence_graph.py
202 203 204 205 206 207 208 209 210 211 212 213 214 | |
_meta()
Returns the metadata of the EvidenceGraph.
(See Agentic.meta().)
Source code in mercury/graph/evidence/evidence_graph.py
184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 | |
_run(request)
Runs the EvidenceGraph with the given request.
(See Agentic.run().)
Source code in mercury/graph/evidence/evidence_graph.py
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 | |
close(endpoint_locked)
Closes the EvidenceGraph, persists it to disk and releases any resources it holds.
(See Agentic.close().)
Source code in mercury/graph/evidence/evidence_graph.py
398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 | |
pilot(intent, just_once=False)
Pilots the EvidenceGraph to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/evidence_graph.py
217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 | |
mercury.graph.evidence.Agent(schema, extra_args, endpoint=None, logger=None)
Bases: Agentic
Agent is a class that connects to an external LLM using litellm and exposes it in the Agentic tree.
Overview
This connects the LLM model in two ways:
-
Upstream: It exposes the Agent's capabilities (defined in the Agents configuration) via an Agentic interface to any Agentic in its Endpoint that can use it as a tool.
-
Downstream: Making the Agent aware of what tools it has access to, and how they work so it can create proper arguments.
Focus and Scope
Agents solve minimal problems, ideally they would be simpler models than LLMs if such models could produce correct output from inputs.
Agents are not aware of the Endpoint architecture, they only "see" what tools have been provided to them via add_tool().
Agents do not validate, prepare, plan their own tasks. The Endpoint is responsible for the "higher-level" orchestration.
These agents do not always communicate through natural language. They use instructions and metadata in natural language but produce structured output that may or may not include natural language.
Interfacing with Agents
Unlike other Agentic, each Agent has only one capability, but you can create as many Agents as needed.
Agents do not directly call a tool. Instead, they produce a litellm (OpenAI-style) answer with finish_reason='tool_calls' and
tool_calls=[ChatCompletionMessageToolCall(function=Function(arguments='{"input": 16}', name='my_tool_for_sqrt'). The Endpoint
calls the tools if the "accounting" of resources is valid and calls the Agent back with result properly appended to the Agent's
conversation. Any accounting of resources for answering a query belongs to the Endpoint.
Defining an Agent
Agents are defined entirely by their configuration (model, capability and tools). Agents are intentionally "narrow" in scope. You can use as many as you want, either exposing them in the Endpoint or letting another Agent use them as a tool.
Configuration of an Agent
(See the file agents.jsonc of an Endpoint newly created using the mge CLI for a working example.)
To connect to any external LLM, possibly providing credentials, note that anything inside the "completion" dictionary will be
passed as arguments to the litellm completion() method in addition to messages and tools (if applicable).
Known Limitations
- For now, calls to Agents to not use parallel execution.
- The management of tool calls is super-simplistic, just a counter to prevent infinite usage.
- There is no safe management of credentials. (You can restrict access to the configuration manually.)
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
schema
|
str
|
a schema (a unique name) to use for the Agent's ID. |
required |
extra_args
|
dict
|
the configuration for the Agent. |
required |
endpoint
|
Agentic
|
an optional Endpoint. It becomes part of the Agent's ID and is available via |
None
|
logger
|
list
|
an optional logger to use for logging events. It must provide an |
None
|
Source code in mercury/graph/evidence/agent.py
84 85 86 87 88 89 90 91 92 93 94 | |
_capability(name, description)
Defines a capability for the agent with the given name and description.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
name
|
str
|
The name of the capability. |
required |
description
|
str
|
A brief description of what the capability does. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
A dictionary representing the capability in the required format. |
Source code in mercury/graph/evidence/agent.py
294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 | |
_dry_run(request)
Simulates running the Agent with the given request.
(See [`Agentic.dry_run()`][mercury.graph.evidence.Agentic.dry_run].)
NOTE:
The Endpoint takes care of validating the request according to the capabilities exposed by the Agent. It is not necessary to validate again here and the Endpoint does not forward the dry_run() request to the Agent. This method is provided as a requirement of the Agentic interface, but it is only used when you use Formalizers directly outside of an Endpoint.
Source code in mercury/graph/evidence/agent.py
190 191 192 193 194 195 196 197 198 199 200 201 | |
_meta()
Returns the metadata of the Agent.
(See Agentic.meta().)
Source code in mercury/graph/evidence/agent.py
162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 | |
_run(request)
Runs the Agent with the given request.
(See Agentic.run().)
Source code in mercury/graph/evidence/agent.py
97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 | |
pilot(intent, just_once=False)
Pilots the Agent to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/agent.py
204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 | |
mercury.graph.evidence.Endpoint(path=None, logger=None, auto_pilot=True)
Bases: Agentic
The Endpoint is the class that serves an entire Agentic architecture to the outside world using the Agentic interface itself.
Overview
An Endpoint is a full project that contains any number of Agentic objects. It's metadata lives in a folder that contains a
mge_endpoint.jsonc file and a file named either mge_endpoint.free or mge_endpoint.locked that acts as a write mutex.
Endpoint is the only object that owns the architecture of the tree.
For any "outside" Agentic user, an Endpoint is just another Agentic service. It is a graph of Agentic objects. There are
two ways to do this: Create as many Endpoints as you want in your Python code and use them as Agentics or, more commonly,
use the mge cli to maintain and serve the Endpoints persisted in the file system as a folder.
What the Endpoint manages
- The definition of the architecture. The Agentic objects are defined here but may be located anywhere.
The architecture can have a life cycle defined by
intentvalues. An intent is a desired state for the architecture. These intents can apply to each Agentic in the architecture. They will typically be ordered integer numbers with names such as "initial", "ready", "busy", etc. defined as an Enum to support human-friendly interfaces (E.g., `mge serve main_doc all_ready 8888'). Negative values represent non recoverable errors, zero is the initial state, and positive values are sorted. Each class can have up to 99 intermediate states below READY which corresponds to 100. Those intents 1..99 represent: building indices, chunking documents, setting up vector databases, formalizing chunks of text, merging into evidence graphs, etc. across multiple Agentic objects. Also, the arrival/update of new documents may require updating the Evidence Graph setting back the state of some of them. So the Endpoint is ALL_READY (100) only when all of them are READY (100). - Saving, loading and parsing (manually edited) its own definition stored in a folder with its name and a
mge_endpoint.jsoncfile. That file also contains configuration of Agentics stored as separate files in the same folder. - A mechanism to
pilotthe Endpoint's state up to a desired state. This is different from therun()method which runs queries using the Endpoint's Agentic interface. Piloting is a process typically done using themgecli. - The mutex providing exclusive write access to the objects. The Endpoint is locked when the cli either serves or pilots the Endpoint.
It can also be done programmatically by calling the
lock()method. This is mandatory when the metadata is modified. - It's own Agentic API. This exposes a set of functions merged from the Agentics that are marked as "exposed" in the Endpoint's conf.
- The interdependencies within the Agentic objects.
Http interface
The cli provides a simple http interface to the Endpoint. The Endpoint exposes its Agentic API like any other Agentic object.
Self configuration
Endpoints can use Agents to complete and verify their configuration with or without human intervention.
Attributes:
| Name | Type | Description |
|---|---|---|
id |
str
|
the Agentic ID of the Endpoint. |
logger |
list
|
the logger to use for logging events. It must provide an |
tools |
dict
|
a dictionary of the Agentics in the Endpoint, keyed by their IDs. |
ids |
dict
|
a dictionary of the Agentics in the Endpoint, keyed by their types. This connects the Agentic from their category and name in the architecture to IDs of the loaded objects. |
states |
Enum
|
an optional Enum class that defines names for the states of an Agentic. It is used to improve readability and cli argument parsing. |
meta |
dict
|
a dictionary of metadata about the Agentic. It is used to store the current state of the Agentic and other information. |
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
the path to the Endpoint's home directory. The final name (the folder inside whatever path) must be identical to its ._normalize_name() value, that is, a name with only letters, numbers or underscores. |
None
|
logger
|
list
|
an optional logger. If not provided, no logging will be done. |
None
|
auto_pilot
|
bool
|
an optional flag that completely disables auto-piloting for such things as forceful unlocking. By default, auto_pilot is set via configuration. |
True
|
Source code in mercury/graph/evidence/endpoint.py
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 | |
__str__()
Returns a console friendly summary of the Endpoint state.
Source code in mercury/graph/evidence/endpoint.py
159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 | |
_dry_run(request)
Runs a dry run of the Endpoint with the given request.
(See Agentic.dry_run().)
Source code in mercury/graph/evidence/endpoint.py
397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 | |
_expose_api()
This method called by pilot() builds the capabilities of the Endpoint by merging the capabilities of all the Agentics that in the 'expose' list of the Endpoint's configuration.
It also checks that all the capabilities have unique names and builds two dictionaries one of capabilities by name and one of
Agentic by capability name. These dictionaries capabilities_by_name and agentic_by_capability are used by the run() and
dry_run() methods.
It also updates the Endpoint's meta with the capabilities.
The pilot() method calls this method when appropriate and sets the state according to the success.
Returns:
| Type | Description |
|---|---|
bool
|
True if no errors found exposing the capabilities, False otherwise. |
Source code in mercury/graph/evidence/endpoint.py
747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 | |
_json_load(fn, recursion_depth=0)
Loads a JSONC file and returns the corresponding object. It also removes comments and recursively loads any referenced JSONC files. The recursion depth is limited to 8 to avoid infinite loops.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
fn
|
str
|
the path to the JSONC file to load. |
required |
recursion_depth
|
int
|
the current recursion depth used internally in recursive calls. |
0
|
Returns:
| Type | Description |
|---|---|
any
|
The object loaded from the JSONC file. |
Source code in mercury/graph/evidence/endpoint.py
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 | |
_link_objects()
This method is called by pilot() when all the Agentic objects have been loaded.
It just looks for what tools each one requires as defined in the configuration field 'tools'. It checks that every tool is found and the resulting graph does not have cycles.
Once that is done, it calls the add_tool() method of each Agentic which required tools to make them available.
The pilot() method calls this method when appropriate and sets the state according to the success.
Returns:
| Type | Description |
|---|---|
bool
|
True if all objects were linked successfully, False otherwise. |
Source code in mercury/graph/evidence/endpoint.py
675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 | |
_load_objects()
This method parses the self.conf dictionary, category by category: sources, ontologies, formalizers, evidence_graphs, agents and custom_agentics.
It creates and instance of each, passing extra arguments to the constructor if they are present in the configuration. These instances are verified to have unique IDs and are stored in self.ids.
The pilot() method calls this method when appropriate and sets the state according to the success.
Returns:
| Type | Description |
|---|---|
bool
|
True if all objects were loaded successfully, False otherwise. |
Source code in mercury/graph/evidence/endpoint.py
585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 | |
_meta()
Returns the metadata of the Endpoint.
(See Agentic.meta().)
Source code in mercury/graph/evidence/endpoint.py
388 389 390 391 392 393 394 | |
_next_agentic_below(intent)
This method is called by pilot() to find the first Agentic in the Endpoint whose state is below the desired intent.
It may be and error state, in which case the Endpoint will set its state to ERR_PILOTING and stop piloting.
The pilot() method calls this method when appropriate and sets the state according to the success.
Returns:
| Type | Description |
|---|---|
Agentic or None
|
The first Agentic whose state is below the desired intent, or None if all Agentics are at or above. |
Source code in mercury/graph/evidence/endpoint.py
811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 | |
_request_issues(request)
Matches the request against the Endpoint's capabilities following the formats described in:
Agentic.run()
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
request
|
dict
|
the request to check. |
required |
Returns:
| Type | Description |
|---|---|
None or str
|
None if the request is valid, or a string describing the issues found. |
Source code in mercury/graph/evidence/endpoint.py
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 | |
_research_capabilities()
This method is called by pilot() when every tool is ready. It builds the new dictionary, self.tools_by_capability (similar to self.agentic_by_capability, but for all the capabilities of all the Agentics instead of just the exposed ones). And adds new entries to self.capabilities_by_name to find every capability of every tool by name. It fails if there are duplicate capability names.
Note: These capabilities are only available as tools calls from the Agentics, they are not exposed via meta['capabilities'].
The pilot() method calls this method when appropriate and sets the state according to the success.
Returns:
| Type | Description |
|---|---|
bool
|
True if all capabilities were researched successfully, False otherwise. |
Source code in mercury/graph/evidence/endpoint.py
829 830 831 832 833 834 835 836 837 838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 857 858 859 860 861 862 863 864 865 866 867 868 869 870 | |
_response_loop(agentic, request, response)
This method is called by _run() to handle the case when an Agentic returns a response with finish_reason = 'tool_calls'.
First it examines if the tool call can be made
- There are enough remaining resources. (According to the configured "max_tool_calls_per_query")
- The tool call expects a tool that is available. (According to self.tools_by_capability)
- It does not check arguments, if it can make the call, the Agentic will check.
If if calling the tool is not allowed or failed, it returns a response with finish_reason = 'error' and a message history that includes the reason for the failure, immediately.
If the tool call works
- It builds or continues a message history as a list of messages.
- It appends the request as a message with the role of the Agent who required, and storing the id of the call if there is one.
- It appends the result of tool call with: 'role': 'tool', 'tool_call_id':
, 'content': - It passes the message history to calling agentic's run() method.
- It stops if the response is not another tool call, or continues the loop if it is.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
agentic
|
Agentic
|
The Agentic that made the initial request. |
required |
request
|
dict
|
The initial request made to the Agentic. |
required |
response
|
dict
|
The initial response from the Agentic. |
required |
Returns:
| Type | Description |
|---|---|
dict
|
The final response from the Agentic after handling tool calls with possibly some error messages if the tool calls failed or were not allowed. |
Source code in mercury/graph/evidence/endpoint.py
873 874 875 876 877 878 879 880 881 882 883 884 885 886 887 888 889 890 891 892 893 894 895 896 897 898 899 900 901 902 903 904 905 906 907 908 909 910 911 912 913 914 915 916 917 918 919 920 921 922 923 924 925 926 927 928 929 930 931 932 933 934 935 936 937 938 939 940 941 942 943 944 945 946 947 948 949 950 951 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 976 977 978 979 980 981 982 983 984 985 986 987 988 989 990 991 992 993 994 995 | |
_run(request)
Runs the Endpoint with the given request.
(See Agentic.run().)
This can raise its own AgenticRunException:
- AgenticFailedToFindCapability When the Endpoint could not identify which Agentic and/or which capability to call.
- AgenticFailedToParseOutput When the Endpoint could not parse the output.
Notes
- Unlike _dry_run(), _run() does not call _request_issues(). The Agentic, not the Endpoint validates the call.
- The Endpoint is responsible of handling Tool calls. If an Agentic (typically an Agent) calls a Tool, the Endpoint has to accept or reject the call based on resources (preventing infinite loops, etc.). If the call is rejected, the Endpoint provides a reason and a message history if possible. If accepted, the Endpoint, calls the tool and then calls the same Agentic with a message history that includes the result of the tool call.
- In an Endpoint with more than one capability (the typical case), the request must be a pure function to identify the Agent. When tool calls are made, conversation becomes a message history assuming Agentics that call tools can behave as (or are) Agents. This is normal behavior, the First tool call is actually passing a message to a function.
Source code in mercury/graph/evidence/endpoint.py
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 | |
close(endpoint_locked)
It calls the close() of each Agentic in the Endpoint. And persists its state to disk.
(See Agentic.close().)
Source code in mercury/graph/evidence/endpoint.py
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 | |
lock(cmd)
Locks or unlocks the Endpoint's mutex. The mutex is used to provide exclusive write access to the Endpoint's metadata for piloting and serving.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
cmd
|
LockState
|
the lock command. It must be one of the values of the |
required |
Returns:
| Type | Description |
|---|---|
LockState
|
the final state of the mutex after the command is executed. A value in the |
Source code in mercury/graph/evidence/endpoint.py
271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 | |
pilot(intent, just_once=False)
Pilots the Endpoint to a new state based on the given intent.
(See Agentic.pilot().)
Source code in mercury/graph/evidence/endpoint.py
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 | |