Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/arg-tech/xaif/llms.txt

Use this file to discover all available pages before exploring further.

xAIF is designed from the ground up for dialogue. Every locution in the argument graph is attributed to a named participant, making speaker identity a first-class part of the data model. This means you can not only reason about what was said and how propositions relate to each other, but also who said what — which is essential for applications like debate analysis, meeting transcription, and argument mining over conversational data.

The Three-Way Mapping

In an xAIF dialogue graph, each spoken turn is represented through three interconnected structures:
  • L-node ("type": "L") — the raw text of the utterance, stored in AIF["nodes"].
  • Locution entry — a record in AIF["locutions"] that maps a nodeID (pointing to the L-node) to a personID (pointing to a participant).
  • Participant entry — a record in AIF["participants"] containing the participantID, firstname, and surname of the speaker.
When you add a locution through add_component, xAIF populates all three automatically. When you load an existing xAIF structure, these three lists are already populated and ready to query.

Checking for Dialogue

Before running any dialogue-specific logic, you can verify that the loaded structure actually contains locution nodes:
aif.is_json_aif_dialog()
This method iterates over AIF["nodes"] and returns True as soon as it finds any node with "type": "L". It returns False for graphs that contain only I-nodes, RA-nodes, and other non-locution types (such as graphs exported from OVA without dialogue data).
from xaif import AIF

aif = AIF("This is a single-speaker text.")
print(aif.is_json_aif_dialog())  # True — raw text init creates an L-node

Looking Up a Speaker

Given any L-node ID, you can retrieve the name and ID of the participant who spoke it:
aif.get_speaker(node_id)
get_speaker builds an internal mapping from every locution entry’s nodeID to its corresponding participant’s full name, then performs a lookup. It returns a tuple of (full_name, participantID):
  • full_name — a string in the form "Firstname Surname".
  • participantID — the integer or string ID from AIF["participants"].
If the node ID is not found in any locution entry, the method returns ("None None", "None").
speaker_name, speaker_id = aif.get_speaker(0)
print(speaker_name)  # e.g. "Speaker 1"
print(speaker_id)    # e.g. 0

Multi-Speaker Example

The example below loads a two-speaker xAIF graph representing a short exchange about SNP party disagreements, then uses the dialogue methods to inspect the structure.
from xaif import AIF

xaif_data = {
  "AIF": {
    "descriptorfulfillments": None,
    "edges": [
      {"edgeID": 0, "fromID": 0, "toID": 4},
      {"edgeID": 1, "fromID": 4, "toID": 3},
      {"edgeID": 2, "fromID": 1, "toID": 6},
      {"edgeID": 3, "fromID": 6, "toID": 5},
      {"edgeID": 6, "fromID": 3, "toID": 9},
      {"edgeID": 7, "fromID": 9, "toID": 7}
    ],
    "locutions": [
      {"nodeID": 0, "personID": 0},
      {"nodeID": 1, "personID": 1}
    ],
    "nodes": [
      {"nodeID": 0, "text": "disagreements between party members are entirely to be expected.", "type": "L"},
      {"nodeID": 1, "text": "the SNP has disagreements.", "type": "L"},
      {"nodeID": 3, "text": "disagreements between party members are entirely to be expected.", "type": "I"},
      {"nodeID": 4, "text": "Default Illocuting", "type": "YA"},
      {"nodeID": 5, "text": "the SNP has disagreements.", "type": "I"},
      {"nodeID": 6, "text": "Default Illocuting", "type": "YA"},
      {"nodeID": 7, "text": "it's not uncommon for there to be disagreements.", "type": "I"},
      {"nodeID": 9, "text": "Default Inference", "type": "RA"}
    ],
    "participants": [
      {"firstname": "Speaker", "participantID": 0, "surname": "1"},
      {"firstname": "Speaker", "participantID": 1, "surname": "2"}
    ],
    "schemefulfillments": None
  },
  "dialog": True,
  "ova": [],
  "text": {"txt": "Speaker 1: disagreements between party members are entirely to be expected. Speaker 2: the SNP has disagreements."}
}

aif = AIF(xaif_data)

print("Is dialogue:", aif.is_json_aif_dialog())
# Is dialogue: True

print("Speaker for node 0:", aif.get_speaker(0))
# Speaker for node 0: ('Speaker 1', 0)

print("Speaker for node 1:", aif.get_speaker(1))
# Speaker for node 1: ('Speaker 2', 1)
The graph here captures that Speaker 1’s locution (node 0) supports an inference to node 7 — “it’s not uncommon for there to be disagreements” — via the RA-node 9. The L-nodes, YA-nodes, and I-nodes are all separate, following the Inference Anchoring Theory structure.

Building a Dialogue from Turns

When constructing a dialogue programmatically rather than loading an existing structure, create_turn_entry is used internally by the AIF initialiser to populate nodes, locutions, and participants from a list of (speaker_name, text) tuples. When called with dialogue=True, the method iterates over each (speaker_name, text) pair and:
  1. Creates an L-node for the turn text.
  2. Appends a locution entry mapping the L-node to the speaker’s person ID.
  3. Appends a participant entry if that speaker has not been seen before.
  4. Increments both the node ID and person ID counters for the next turn.
This is the mechanism that runs under the hood when you pass a list of turns to your own wrapper. The dialogue=False path (used when AIF is initialised with a plain string) produces a single L-node attributed to a synthetic "Default Speaker" participant.

Accessing All Participants

Once a graph is loaded or built, the full participant list is available directly on the xAIF dictionary:
participants = aif.xaif["AIF"]["participants"]
for p in participants:
    print(p["participantID"], p["firstname"], p["surname"])
You can also iterate over aif.xaif["AIF"]["locutions"] to get the full node-to-person mapping and cross-reference it with the participants list for bulk speaker attribution.

Build docs developers (and LLMs) love