context-weaver 0.1.1

(WIP) A procedural text evaluation engine
Documentation

ContextWeaver

Warning: This project is in an early development stage. Breaking changes will be frequent.

ContextWeaver is a (WIP) framework for dynamically managing and injecting “lore” into text-generation contexts (e.g., LLM prompts, game scripts, dynamic UIs). It uses entries with activation conditions plus inline processors to produce context-aware, procedurally generated text.


✨ Core Concepts

  1. 📄 Context Injection
    Each entry contains a payload of text (“lore”) and one or more activation conditions. When a condition is met, that text is inserted into the target context (for example, an LLM prompt or chat history).
    Activation conditions: see Roadmap.

  2. ⚙️ Dynamic Content via Processors
    Entries can include inline “processors”—small snippets of logic that run at evaluation time and replace themselves with generated output. This makes your injected text dynamic rather than static.


💡 Processors

Processors embed procedural logic directly in your entries using a familiar, JSON-like syntax.

Syntax

  • Basic call:
    @[weaver.core.rng(min: 0, max: 100)]
    
  • Pseudo-JSON properties use unquoted keys and allow inline comments:
    {
      property: "value", // no quotes around property
    }
    
  • Tag attributes:
    <trigger id=0>
    

Example: Random Number

The generated number is: @[weaver.core.rng(min: 0, max: 100)]

The generated number is: 73

Nesting

You can nest processors arbitrarily:

@[weaver.core.wildcard(
  items: [
    "Weather: @[weaver.core.wildcard(items:[\"sunny\",\"cloudy\",\"rainy\"])]",
    "Number: @[weaver.core.rng(min:0,max:100)]"
  ]
)]

Evaluation steps:

  1. Inner processors resolve first (e.g. picks "cloudy" or a random number).
  2. Outer wildcard then picks among the now-concrete strings.

📝 Variables

Use variables to inject key-value data at evaluation time.

  • Syntax: {{SCOPE:VAR_NAME}}
  • Scopes:
    • global — accessible everywhere
    • ENTRY_ID — only within that entry
  • Type note: Types are inferred at runtime. Core processors handle mismatches gracefully, but custom logic should guard against bad types.

🔄 Macros

Macros let you write control flow in your entries. They live in {# ... #} blocks.

If-Macro

{# if CONDITION #}
  …true branch…
{# else #}
  …false branch…
{# endif #}
  • Supports binary and arithmetic operators, grouping, and variable interpolation.
  • Built-in helper functions:
    Function Input Returns
    len(x) String, List, HashMap Integer length
    contains(x,y) String/List & String Boolean membership

Foreach-Macro

{# foreach ITEM in COLLECTION #}
  …use {{ITEM}}…
{# endforeach #}

Iterate over lists, maps, or strings; collections may mix types.


🔌 Plugin Support

Define your own processors via a bridge interface. Below is a Rust example.

Bridge Trait (Rust)

use context_weaver::core::processors::PluginBridge;
use serde_json::Value;

struct MyBridge;

impl PluginBridge for MyBridge {
    type PluginId = u32;

    fn invoke_plugin(
        &self,
        plugin_id: Self::PluginId,
        properties: Value
    ) -> Result<String, WorldInfoError> {
        match plugin_id {
            0 => dummy_logic(properties),
            _ => Err(WorldInfoError::PluginError("Unknown plugin".into())),
        }
    }
}

Dummy Plugin Logic

fn dummy_logic(props: Value) -> Result<String, WorldInfoError> {
    // Parse input properties carefully
    let items = props.get("items").and_then(Value::as_array)
        .ok_or(WorldInfoError::PluginError("'items' missing".into()))?;

    let choice = items
        .iter()
        .filter_map(Value::as_str)
        .choose(&mut rand::thread_rng())
        .ok_or(WorldInfoError::PluginError("No valid items".into()))?;
    Ok(format!("Forecast: {}", choice))
}

Register & Use

let registry = ProcessorRegistry::new(Arc::new(MyBridge));
registry.register_plugin_processor("dummy", "forecast");

let input = r#"@[weaver.plugin.dummy.forecast(
  plugin_author:"dummy",
  plugin_name:"forecast",
  plugin_id:0,
  plugin_data:{items:["sunny","rainy"]}
)]"#;

let mut wi = WorldInfo::new(Box::new(registry));
wi.insert_entry(WorldInfoEntry::create("e1", 0).with_text(input));

let result = wi.evaluate()?;
println!("{}", result); // e.g. "Forecast: rainy"

Naming convention: weaver.plugin.<author>.<name>


🛠️ Installation

Cargo

cargo add context-weaver

From Source

git clone https://github.com/Cruxial0/context-weaver.git
# Add to your Cargo.toml as a workspace member or dependency path

🚀 Usage Scenarios

  • LLM prompt orchestration
  • Text-based games: random NPCs, scenarios
  • Dynamic UIs: ComfyUI, AUTOMATIC1111 prompts
  • Any app needing runtime-computed text

🚧 Roadmap

Processor & Activation

  • ✅ Plugin support
  • ✅ Various property types
  • ❌ Documents ([[DOCUMENT_ID]])
  • ✅ Keyword/regex/recursive activation

Persistence

  • ❌ JSON save/load of state

Context Insertion

  • ❌ Configurable strategies & frequency

Macro & Templating

  • ✅ If-blocks
  • ✅ Scoped variables
  • ❌ Extended conditionals, variable mutation, piping

Syntax & Parsing

  • ✅ Pseudo-JSON syntax via Pest
  • ✅ Structured parsing & diagnostics
  • ✅ Clear error messages
Construct Syntax Supported
Processor @[processor.name(props)] Yes
Trigger <trigger id=…> Yes
If-Macro {# if … #}…{# endif #} Yes
Foreach-Macro {# foreach … #}…{# endforeach #} Yes
Variable mutation @[set], @[modify], etc. No
Documents [[DOCUMENT_ID]] Kinda (no)

🤝 Contributing

Contributions are welcome! Please open issues or PRs on the GitHub repository.