Skip to content

Commit bad6fdb

Browse files
added comm protocol concept doc and updated event doc
1 parent a3fa17c commit bad6fdb

4 files changed

Lines changed: 280 additions & 116 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.0+2025.09.23T21.21.23.699Z.dfa95f90.master
1+
0.1.0+2025.09.25T00.00.42.365Z.a3fa17c9.master

autodocs/concepts.rst

Lines changed: 34 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,42 @@ Concepts
22
=============
33

44
Explanations of key ideas, principles, and background knowledge.
5+
Follow this recommended sequence to build context before diving into
6+
implementation details:
7+
8+
- :doc:`History <docs/concepts/history>` - establishes the background and
9+
problem space the project is addressing.
10+
- :doc:`System Design <docs/concepts/system_design>` - explains how the product
11+
strategy and user needs translate into an overall system approach.
12+
- :doc:`Architecture <docs/concepts/architecture>` - outlines the concrete
13+
architecture that implements the system design.
14+
- :doc:`Technologies <docs/concepts/technologies>` - surveys the primary tools
15+
and platforms we rely on to realize the architecture.
16+
- :doc:`Events <docs/concepts/events>` - introduces the event model that drives
17+
data flowing through the system.
18+
- :doc:`Reducers <docs/concepts/reducers>` - details how incoming events are
19+
aggregated into the state our experiences depend on.
20+
- :doc:`Communication Protocol <docs/concepts/communication_protocol>` - discusses how
21+
the system queries data from reducers for dashboards.
22+
- :doc:`Scaling <docs/concepts/scaling>` - covers strategies for growing the
23+
system once the fundamentals are in place.
24+
- :doc:`Auth <docs/concepts/auth>` - describes authentication considerations
25+
that secure access to the system.
26+
- :doc:`Privacy <docs/concepts/privacy>` - documents how we protect learner data
27+
and comply with privacy expectations.
528

629
.. toctree::
30+
:hidden:
731
:maxdepth: 1
832
:titlesonly:
933

10-
docs/concepts/architecture.md
11-
docs/concepts/auth.md
12-
docs/concepts/events.md
13-
docs/concepts/history.md
14-
docs/concepts/privacy.md
15-
docs/concepts/reducers.md
16-
docs/concepts/scaling.md
17-
docs/concepts/system_design.md
18-
docs/concepts/technologies.md
34+
docs/concepts/history
35+
docs/concepts/system_design
36+
docs/concepts/architecture
37+
docs/concepts/technologies
38+
docs/concepts/events
39+
docs/concepts/reducers
40+
docs/concepts/communication_protocol
41+
docs/concepts/scaling
42+
docs/concepts/auth
43+
docs/concepts/privacy
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# Communication Protocol
2+
3+
The communication protocol is Learning Observer's query and transport
4+
layer. It allows dashboards, notebooks, and other clients to request
5+
aggregated data from the key-value store and supporting services by
6+
submitting a declarative *execution DAG* (directed acyclic graph). The
7+
server evaluates the DAG node-by-node, resolves the required
8+
parameters, executes reducers or helper functions, and returns the
9+
assembled result. This document explains how that process fits
10+
together, the core building blocks you can use in a query, and the
11+
helper utilities that make it easier to integrate those queries into
12+
applications.
13+
14+
## Lifecycle of a Request
15+
16+
1. **Query construction** - A client builds a nested query description
17+
in Python (or another language) with the helpers in
18+
`learning_observer.communication_protocol.query`. The helpers mirror
19+
relational concepts such as parameters, joins, and projections and
20+
produce JSON-serialisable dictionaries. (See: query.py L1-L123)
21+
2. **Flattening** - Before execution, the DAG is normalised so every
22+
node has a unique identifier and can reference other nodes via
23+
`variable` pointers. The `flatten` utility rewrites nested
24+
structures such as `select(keys(...))` into separate nodes to make
25+
evaluation straightforward. (See: util.py L1-L59)
26+
3. **Execution** - The executor walks the flattened DAG, dispatching
27+
each node type to a registered handler. Nodes can call Python
28+
functions, fetch keys from the key-value store, join intermediate
29+
datasets, or map functions across collections. The executor
30+
assembles the final payload and enforces error handling through the
31+
`DAGExecutionException` type. (See: executor.py L1-L145, L147-L220)
32+
4. **Exports** - Queries expose named *exports* that identify the DAG
33+
nodes clients may request. The integration layer can bind those
34+
exports to callables so dashboards or notebooks can invoke them as
35+
regular async functions. (See: util.py L64-L104, integration.py L38-L102)
36+
37+
This flow supports both server-defined queries and open-ended
38+
exploration. Production deployments typically offer curated, predefined
39+
queries while development tooling exposes the full language for
40+
experimentation. (See: README.md L11-L36)
41+
42+
## Core Node Types
43+
44+
Every node in the execution DAG has a `dispatch` type that determines
45+
how the executor evaluates it. The query helper functions generate the
46+
correct shape for each node type. (See: query.py L19-L123) The most common nodes are:
47+
48+
- **`parameter`** - Declares a runtime argument. Parameters can be
49+
required or optional, and the executor substitutes provided values or
50+
defaults before downstream nodes run. (See: query.py L33-L42, executor.py L114-L144)
51+
- **`variable`** - References the output of another node in the DAG.
52+
These indirections are automatically inserted during flattening but
53+
can also be used explicitly when wiring complex queries. (See: query.py L45-L52, util.py L13-L61)
54+
- **`call`** - Invokes a published Python function on the server.
55+
Functions are registered with `publish_function`, which ensures every
56+
callable has a unique name. Called functions may be synchronous or
57+
asynchronous; the executor awaits results as needed. (See: query.py L55-L67, executor.py L61-L112, integration.py L21-L47)
58+
- **`keys`** - Produces the key descriptions required to fetch reducer
59+
outputs from the key-value store. Keys nodes typically wrap the
60+
outputs of roster or metadata queries so downstream `select` nodes
61+
can retrieve the associated reducer documents. (See: query.py L114-L123, util.py L72-L102)
62+
- **`select`** - Retrieves documents from the key-value store for the
63+
provided keys. You can request all fields or limit to specific
64+
projections via `SelectFields` enumerations. (See: query.py L70-L83)
65+
- **`join`** - Merges two lists of dictionaries on matching keys using
66+
dotted-path lookups. Left rows are preserved even without a matching
67+
right-hand record, making it straightforward to enrich reducer
68+
outputs with roster data. (See: query.py L86-L96, executor.py L147-L220)
69+
- **`map`** - Applies a published function to each value in a list,
70+
optionally in parallel, returning the transformed collection. This is
71+
useful for server-side post-processing or feature extraction before a
72+
result is exported. (See: query.py L99-L111)
73+
74+
## Building Queries Efficiently
75+
76+
Writing DAGs by hand is verbose, so the protocol provides shorthands
77+
for common access patterns. For example,
78+
`generate_base_dag_for_student_reducer` returns an execution DAG that
79+
retrieves the latest reducer output for every student in a course,
80+
including roster metadata and a preconfigured export entry. Dashboards
81+
use this helper to quickly expose reducer results without writing the
82+
full DAG each time. (See: util.py L63-L101)
83+
84+
The `integration` module can also bind exports directly to a module so
85+
code can call `await module.student_event_counter_export(course_id=...)`
86+
instead of manually constructing requests. This keeps the protocol's
87+
flexibility while offering ergonomic entry points for UI
88+
components. (See: integration.py L49-L102)
89+
90+
## Tooling and Debugging
91+
92+
Two exploratory tools live alongside the protocol implementation:
93+
94+
- `debugger.py` - Provides an interface for submitting ad-hoc queries
95+
and inspecting intermediate results.
96+
- `explorer.py` - Lists predefined queries already published on the
97+
server so you can execute them interactively.
98+
99+
Because the protocol is evolving, these tools occasionally require
100+
updates when the underlying schema changes. Keeping the communication
101+
protocol documented and covered by tests makes it easier to spot and
102+
fix those regressions quickly. (See: README.md L45-L72)
103+
104+
## Security Considerations
105+
106+
Production deployments default to predefined queries so clients can
107+
only request vetted datasets. Open-query mode should be restricted to
108+
trusted environments—such as local notebooks or read replicas—because
109+
it allows arbitrary function calls and joins that may expose sensitive
110+
information or stress backing stores. (See: README.md L11-L36)
111+
112+
Understanding these concepts makes it easier to extend the protocol,
113+
design new reducers, and reason about the performance characteristics
114+
of dashboards built on Learning Observer.

0 commit comments

Comments
 (0)