Skip to content

Commit db45c47

Browse files
author
MesTTo
committed
feat(das-client): connect to a LIVE DAS over gRPC — version-matched AtomSpaceNode contract, ping -> PING (real end-to-end)
1 parent 680e96a commit db45c47

6 files changed

Lines changed: 321 additions & 11 deletions

File tree

packages/das-client/README.md

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,28 @@ Distributed AtomSpace (DAS) as a pluggable `Space` backend for MeTTa TS.
1212
- **`das.metta`** — the vendored DAS module type declarations (`new-das!`, `das-evolution!`, …).
1313
- The bus command vocabulary (`BusCommand`) read from the Python `service_bus`.
1414

15-
## The one remaining boundary (needs a live DAS to validate)
15+
## Validated against a live DAS
1616

17-
What's left is the **streaming pattern-matching choreography**: the proxy joins the network, issues a `PATTERN_MATCHING_QUERY` over `bus_command_proxy`, and assembles the streamed `query_answer_tokens_flow` — specifically the `ANSWER_BUNDLE`/`COUNT` token decoding into `QueryAnswer` bindings (`hyperon_das/service_clients/pattern_matching_query.py`).
17+
A real DAS cluster was stood up and the TypeScript client called it over gRPC end-to-end:
1818

19-
This is intentionally **not** shipped as an unverified best-guess. Its exact wire format (token framing, answer encoding) can only be validated byte-for-byte against a running DAS (`das-toolbox`) or recorded real traffic — porting it blind would violate the project's correctness-first rule (don't ship sound-but-unverified code as if it works). The pieces it composes (gRPC transport, bus node, handle hashing, command vocabulary) are all real and tested above; finishing it is: stand up a DAS, capture/observe the answer-token frames, port the decoder, add the live differential test.
19+
```bash
20+
# 1.0.0 das-cli matches the released agent image; MongoDB 7.0 avoids the 8.x kernel guard.
21+
pip install -e das-toolbox/das-cli # at tag 1.0.0
22+
das-cli config set # accept defaults
23+
das-cli db start # Redis :40020 + MongoDB :40021 (use mongo:7.0)
24+
das-cli metta load animals.metta
25+
das-cli ab start # Attention Broker :40001
26+
das-cli qa start # Query Agent :40002 (owns `pattern_matching_query`)
27+
28+
DAS_LIVE=1 pnpm vitest run packages/das-client/src/live-das.test.ts
29+
# LIVE DAS AtomSpaceNode.ping -> {"error":false,"msg":"PING"}
30+
```
31+
32+
**Version-matching is the catch.** The released 1.0.0 Query Agent serves the `dasproto.AtomSpaceNode` service; `das-proto` HEAD later renamed it to `DistributedAlgorithmNode`. Calling HEAD's contract returns gRPC `UNIMPLEMENTED`; generating from the pre-rename `atom_space_node.proto` makes the call land (`ping``PING`). So the client carries both generated contracts and the live test uses the one the running agent actually serves.
33+
34+
## The one remaining boundary (the query-token protocol)
35+
36+
With gRPC connectivity to a live agent now proven, what's left is the **DAS query-token language + answer decode**: turn a MeTTa pattern into DAS query tokens (`LINK_TEMPLATE`/`NODE`/`VARIABLE` + handles), issue it via the proxy's `execute_message`, and decode the streamed `ANSWER_BUNDLE` tokens (`QueryAnswer.untokenize`, then resolve handles against the atomdb). The Python reference is `hyperon_das/service_clients/pattern_matching_query.py` + `query_answer.py`. This is now **unblocked** — a live DAS is the oracle — but it is a substantial, version-sensitive port (DAS's query path is itself marked *Under Construction* upstream), so it is built incrementally against the live agent rather than shipped as an unverified best-guess.
2037

2138
**Node-only:** a participant hosts an inbound bus node, which a browser cannot do — the browser reaches DAS through [`@metta-ts/das-gateway`](../das-gateway).
2239

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
syntax = "proto3";
2+
import "common.proto";
3+
package dasproto;
4+
5+
message MessageData {
6+
string command = 1;
7+
repeated string args = 2;
8+
string sender = 3;
9+
bool is_broadcast = 4;
10+
repeated string visited_recipients = 5;
11+
}
12+
13+
service AtomSpaceNode {
14+
rpc ping (Empty) returns (Ack) {}
15+
rpc execute_message (MessageData) returns (Empty) {}
16+
}

packages/das-client/src/gen/atom_space_node.ts

Lines changed: 254 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/das-client/src/gen/common.ts

Lines changed: 12 additions & 8 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/das-client/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@
1313
export { type DasTransport, MockTransport } from "./transport";
1414
export { DasSpace } from "./das-space";
1515
export { computeHash, namedTypeHash, terminalHash, compositeHash, expressionHash } from "./handle";
16+
export { BusNode, BusCommand, type MessageHandler } from "./bus-node";
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { describe, it, expect } from "vitest";
2+
import { credentials } from "@grpc/grpc-js";
3+
import { AtomSpaceNodeClient } from "./gen/atom_space_node";
4+
5+
// Live integration: ping the running DAS Query Agent on :40002 over its actual (pre-rename)
6+
// AtomSpaceNode gRPC contract. Skipped unless a DAS is up (DAS_LIVE=1).
7+
const run = process.env.DAS_LIVE === "1" ? it : it.skip;
8+
describe("live DAS", () => {
9+
run("pings the real Query Agent over gRPC", async () => {
10+
const c = new AtomSpaceNodeClient("127.0.0.1:40002", credentials.createInsecure());
11+
const ack = await new Promise<{ error: boolean; msg: string }>((res, rej) =>
12+
c.ping({}, (e, a) => (e ? rej(e) : res(a))),
13+
);
14+
c.close();
15+
console.log("LIVE DAS AtomSpaceNode.ping ->", JSON.stringify(ack));
16+
expect(ack).toBeDefined();
17+
});
18+
});

0 commit comments

Comments
 (0)