Sessions

Adding turns

How to add conversation turns and interpret the structured extraction result.

A turn is a single message in a conversational thread. After a session is open, every message – whether from the user, the assistant, the system, or a tool – is recorded as a turn. Spectron processes each turn synchronously through an extraction pipeline and returns a structured diff of everything that was learned or changed.

POST /api/v1/{context_id}/sessions/{session_id}/turns
Content-Type: application/json

{
"role": "user",
"content": "I just got promoted to CTO"
}

The request blocks until extraction is complete, then returns the structured result. Extraction is synchronous by design: the caller can use the returned diff to update application state, drive real-time UI, or trigger downstream logic without polling.

RoleWhen to use
userMessages from the human participant. Extraction is most active on user turns.
assistantResponses generated by the LLM. Recording assistant turns preserves the full conversational context and allows Spectron to track what the assistant has stated or committed to.
systemSystem prompt or instruction turns. Spectron extracts behavioural instructions and constraints from system turns.
toolResults returned from tool calls. Spectron extracts factual content from tool results and attributes it to the tool source.

All four roles participate in provenance. Each turn is stored with its role, and every fact extracted from it carries a source_turn reference regardless of role.

When a turn arrives, Spectron runs the following steps synchronously before returning:

  1. Named entity recognition – identifies people, organisations, locations, products, and custom entity types configured in your context.

  2. Attribute extraction – extracts scalar properties of entities (job title, location, preference, status, and so on).

  3. Relation extraction – identifies directed relationships between entities.

  4. Instruction detection – recognises statements that express user preferences, behavioural instructions, or explicit requests about how the agent should behave.

  5. Uncertainty flagging – marks statements that are speculative, negated, or otherwise of low confidence.

  6. Reconciliation – compares newly extracted facts against existing memory. Facts that contradict prior records are resolved and a correction record is produced. See Reconciliation and supersession for the full correction model.

result = await session.turn(role="user", content="I just got promoted to CTO")
const result = await session.turn({ role: "user", content: "I just got promoted to CTO" });

The response from a turn submission contains the full extraction diff for that turn.

{
"turn_id": "turn:01hy2…",
"entities": [
{ "id": "entity:01hy2a…", "type": "person", "label": "Alice", "source_turn": "turn:01hy2…" }
],
"attributes": [
{
"id": "attr:01hy2b…",
"entity": "entity:01hy2a…",
"key": "job_title",
"value": "CTO",
"valid_from": "2026-05-12T16:24:00Z",
"source_turn": "turn:01hy2…"
}
],
"relations": [],
"instructions": [],
"uncertainties": [],
"corrections": []
}

The stable identifier for this turn. Store it if you need to request a diff from /state/diff?since={turn_id} later.

Entities named or implied in the turn content. Each entity carries:

  • id – stable record identifier

  • type – entity type (e.g. person, organisation, project)

  • label – the canonical display name

  • source_turn – the turn that first introduced this entity

An entity record is created once and then referenced by subsequent attributes and relations. If an entity was already known, the existing record is returned rather than a duplicate.

Scalar facts about entities. Each attribute carries:

  • entity – the entity the attribute belongs to

  • key – the attribute name (e.g. job_title, location, preference)

  • value – the extracted value

  • valid_from – when the attribute became valid (defaults to the turn timestamp)

  • valid_until – when the attribute ceased to be valid, if known

  • source_turn – the turn that produced this attribute

Attributes are versioned. If the same key already exists for an entity and the new value differs, the prior attribute is superseded rather than overwritten. The supersession chain is preserved so the history of a fact is always queryable.

Directed relationships between two entities. Each relation carries:

  • from – source entity

  • to – target entity

  • type – relationship type (e.g. works_at, manages, owns)

  • source_turn – the turn that established the relation

Behavioural directives extracted from the turn. These are statements where the user or system is expressing a persistent preference or rule, such as "always reply in French" or "never mention competitors by name." Instructions are surfaced in the context payload and injected into prompts automatically when using the managed chat loop.

Each instruction carries:

  • text – the normalised instruction text

  • priority – extracted priority level (high, medium, low)

  • source_turn – the turn that introduced the instruction

Statements extracted from the turn that Spectron cannot assert with confidence – negated claims, speculative language, conflicting signals, or information that requires human verification. These are stored separately from confirmed attributes and are surfaced in the state under unknowns.

When reconciliation finds that a newly extracted fact contradicts an existing attribute, a correction record is produced. Each correction carries:

  • attribute – the attribute identifier that was corrected

  • previous – the prior value and its source turn

  • current – the new value and this turn as source

  • timestamp – when the correction occurred

Corrections are the mechanism through which Spectron handles conversational drift – for example, when a user corrects a name, updates their role, or revises a stated preference. See Reconciliation and supersession for how the supersession chain works.

Because the extraction result returns synchronously with the turn response, you can use it to drive application state immediately after each message is submitted.

A common pattern is to compare the returned arrays against your local state and highlight changes in the UI:

result = await session.turn(role="user", content="Actually I moved to Berlin last month")

for attr in result.attributes:
ui.render_attribute(attr.entity, attr.key, attr.value)

for correction in result.corrections:
ui.strike_through(correction.previous.value)
ui.highlight_new(correction.current.value)
const result = await session.turn({ role: "user", content: "Actually I moved to Berlin last month" });

for (const attr of result.attributes) {
ui.renderAttribute(attr.entity, attr.key, attr.value);
}

for (const correction of result.corrections) {
ui.strikeThrough(correction.previous.value);
ui.highlightNew(correction.current.value);
}

When you are driving the LLM loop yourself (as opposed to using session.chat()), record the assistant's response as a turn after each generation:

user_result = await session.turn(role="user", content=user_message)

context = await session.context(query=user_message)
reply = your_llm.generate(system=context.context, user=user_message)

assistant_result = await session.turn(role="assistant", content=reply)

Recording assistant turns preserves the full conversational record and allows Spectron to extract any facts the assistant stated – commitments, stated knowledge, or preferences expressed on behalf of the agent. Omitting assistant turns is permitted, but the resulting memory will have gaps that could reduce context quality on subsequent retrieval.

To retrieve all turns recorded in a session:

GET /api/v1/{context_id}/sessions/{session_id}/turns
turns = await session.turns()
const turns = await session.turns();

The response is an ordered list of turns with their roles, content, and timestamps. Extraction results are not re-included in this listing; use the /state endpoint to query the facts that were produced.

Was this page helpful?