npm package: @garrix82/reactgenie-dsl
Description: A natural language parser based on a large language model
Author: Omkar Mirgal
Latest version: 1.0.6

# ReactGenieDSL

> Decorators, DSL execution, and LLM-backed semantic parsing for ReactGenie-style applications

> Fork notice: this package snapshot is maintained as a thesis-project fork by [Omkar Mirgal](https://github.com/OmkarMirgal). For the original ReactGenieDSL project, see [StanfordHCI/ReactGenieDSL](https://github.com/StanfordHCI/ReactGenieDSL).

## Introduction

`@omkarfork/reactgenie-dsl` is the lower-level engine that powers Genie-based apps.

It provides:

- a decorated object model for app state and actions
- a shared Redux-backed Genie store
- a DSL interpreter for explicit command execution
- an LLM-backed semantic parser that translates natural language into DSL
- structured response generation for spoken and on-screen feedback
- monitoring hooks for parser and response calls

If you need the React integration layer, voice UI, route handling, or screen-aware `Current()` resolution, use `@omkarfork/reactgenie-lib` on top of this package.

This README covers installation, quick start, feature overview, and the current public surface. For deeper implementation details and parser/runtime behavior, see [DEVELOPER_GUIDE.md](./DEVELOPER_GUIDE.md).

## Feature Overview

ReactGenieDSL currently includes:

- Genie model base classes: `GenieObject`, `DataClass`, `HelperClass`
- decorators: `GenieClass`, `GenieFunction`, `GenieProperty`, `GenieKey`
- descriptor types: `ClassDescriptor`, `FuncDescriptor`, `FieldDescriptor`, `ParamDescriptor`
- shared store bootstrap and access: `initGenie`, `genieDispatch`, `sharedStore`, `sharedState`, `AllGenieObjects`
- DSL execution with `DslInterpreter`
- stepwise execution introspection with `interpretSteps(...)`
- JSON-friendly result shaping with `describe(...)` and `describeSteps(...)`
- built-in collection operators:
  - `matching(...)`
  - `contains(...)`
  - `between(...)`
  - `equals(...)`
  - `sort(...)`
  - `index(...)`
  - `length()`
- natural-language orchestration with `NlInterpreter`
- parser backends:
  - `NlParserGroq`
  - `NlParser` for OpenAI-compatible APIs
- prompt generators:
  - `BasicPromptGen`
  - `DescriptorPromptGen`
  - `ContextAwareDescriptorPromptGen`
- parser monitoring with `getLLMMonitor(...)`
- configurable sampling and provider selection
- structured agent-response generation with normalized fallback handling

## Parser Enhancements In The Current Implementation

The current parser stack includes several implementation details beyond the older basic prompt flow:

- context-aware prompt narrowing that selects only relevant class descriptions and examples
- lexical ranking of descriptors/examples without an embeddings dependency
- bounded caching of prompt selections for repeated utterances
- recent-turn memory plus summarized older-turn context
- runtime UI context injection for deictic utterances such as "this", "it", or "current"
- explicit parser rules for:
  - named parameters
  - array-parameter brackets
  - dotted accessor parameters such as `.receivedAt`
- structured JSON output for command parsing and agent responses
- lenient-first then strict structured-output retries
- semantic validation of generated commands through a validator interpreter
- automatic repair passes when the generated DSL is semantically invalid
- optional stateful validation retry for the OpenAI Responses path
- normalized response payloads and safe fallback responses when response synthesis fails
- LangSmith-compatible monitoring hooks for prompt, token, latency, and cost tracking

## Installation

Install the current package and `reflect-metadata`:

```bash
npm install @omkarfork/reactgenie-dsl reflect-metadata
```

## TypeScript and Babel setup

ReactGenieDSL relies on decorator metadata.

TypeScript:

```json
{
  "compilerOptions": {
    "experimentalDecorators": true,
    "emitDecoratorMetadata": true,
    "useDefineForClassFields": false
  }
}
```

Babel:

```js
plugins: [
  ["@babel/plugin-proposal-decorators", { legacy: true }],
  ["@babel/plugin-transform-class-properties", { loose: true }],
  "babel-plugin-parameter-decorator",
  "babel-plugin-reactgenie"
]
```

Import `reflect-metadata` before any decorated Genie classes are evaluated.

## Quick Start

### 1. Define a Genie model

```ts
import "reflect-metadata";
import {
  DataClass,
  GenieClass,
  GenieFunction,
  GenieKey,
  GenieProperty,
  int,
} from "@omkarfork/reactgenie-dsl";

@GenieClass("A counter the user can inspect or update")
export class Counter extends DataClass {
  @GenieKey
  @GenieProperty("Stable counter identifier")
  name: string;

  @GenieProperty("Current count")
  count: int;

  static Examples = [
    {
      userUtterance: {
        en: "increment the apples counter",
        de: "erhoehe den apples Zaehler",
      },
      exampleParsed: 'Counter.findByName(name: "apples").increment()',
    },
    {
      userUtterance: {
        en: "what is the apples count",
        de: "wie ist der apples Zaehlerstand",
      },
      exampleParsed: 'Counter.findByName(name: "apples").count',
    },
  ];

  constructor({ name, count = 0 }: { name: string; count?: int }) {
    super({ name });
    this.name = name;
    this.count = count;
  }

  static setup() {
    Counter.CreateObject({ name: "apples", count: 2 });
    Counter.CreateObject({ name: "oranges", count: 5 });
  }

  @GenieFunction("Return the default counter")
  static current(): Counter {
    return Counter.GetObject({ name: "apples" });
  }

  @GenieFunction("Find a counter by name")
  static findByName({ name }: { name: string }): Counter {
    return Counter.GetObject({ name });
  }

  @GenieFunction("Increase the counter by one")
  increment(): this {
    this.count += 1;
    return this;
  }
}
```

Notes:

- use `DataClass` for indexed, retrievable entities
- use `HelperClass` for nested value objects
- create objects with `CreateObject(...)`, not `new`
- `@GenieFunction()` currently expects either no parameters or a single destructured object parameter

### 2. Initialize Genie

```ts
import { initGenie } from "@omkarfork/reactgenie-dsl";

const store = initGenie();
```

`initGenie()` initializes the shared store, applies registered modifiers, and runs `setup()` on already-registered classes.

### 3. Execute explicit DSL

```ts
import { AllGenieObjects, DslInterpreter } from "@omkarfork/reactgenie-dsl";

const descriptors = Object.values(AllGenieObjects).map(
  (klass: any) => klass.ClassDescriptor
);

const interpreter = new DslInterpreter(descriptors);

await interpreter.interpret('Counter.findByName(name: "apples").increment()');
const value = await interpreter.interpret('Counter.findByName(name: "apples").count');
const described = await interpreter.describe(value);
```

### 4. Parse natural language into DSL

```ts
import {
  AllGenieObjects,
  ClassDescriptor,
  ExampleParse,
  GenieObject,
  NlInterpreter,
} from "@omkarfork/reactgenie-dsl";

const descriptors = Object.values(AllGenieObjects).map(
  (klass: any) => klass.ClassDescriptor as ClassDescriptor<GenieObject>
);

const examples: ExampleParse[] = [
  new ExampleParse(
    { en: "increment the apples counter", de: "erhoehe den apples Zaehler" },
    'Counter.findByName(name: "apples").increment()'
  ),
  new ExampleParse(
    { en: "what is the apples count", de: "wie ist der apples Zaehlerstand" },
    'Counter.findByName(name: "apples").count'
  ),
];

const interpreter = new NlInterpreter(
  descriptors,
  process.env.LLM_API_KEY!,
  undefined,
  examples
);

const executionResult = await interpreter.interpret("increment the apples counter");
```

### 5. Generate a structured response

```ts
const response = await interpreter.respond(
  "increment the apples counter",
  'Counter.findByName(name: "apples").increment()',
  JSON.stringify(await interpreter.dslInterpreter.describe(executionResult))
);
```

The response payload shape is:

```ts
{
  minimalText: string;
  fullText: string;
  type: "info" | "error" | "success" | "warning";
  shouldSpeak: boolean;
}
```

## Current DSL Capabilities

ReactGenieDSL currently supports:

- static calls such as `Order.current()`
- instance calls such as `Order.current().placeOrder()`
- property access such as `Order.current().status`
- chained access such as `Order.All().sort(field: .dateTime, ascending: false)[0].restaurant.name`
- array literals
- negative indexing
- collection filters through `matching`, `contains`, `between`, and `equals`
- collection sorting through `sort`
- collection indexing through `index(...)` or bracket syntax
- collection length via `length()`

Important parser rules:

- parameter names must be explicit
- accessor parameters for filters/sort must be dotted accessors such as `.name`, not quoted strings
- when passing an array expression into a function parameter, the expression must be wrapped in `[...]`

## Provider Configuration

`NlInterpreter` supports `groq` and `openai` parser providers.

### Groq example

```ts
const interpreter = new NlInterpreter(
  descriptors,
  process.env.LLM_API_KEY!,
  undefined,
  examples,
  undefined,
  {
    parser: {
      provider: "groq",
      groq: {
        model: "llama-3.3-70b-versatile",
        baseUrl: "https://api.groq.com/openai/v1",
        samplingParams: {
          temperature: 0.2,
          top_p: 1,
          frequency_penalty: 0,
          presence_penalty: 0,
        },
      },
    },
  }
);
```

### OpenAI-compatible example

```ts
const interpreter = new NlInterpreter(
  descriptors,
  process.env.LLM_API_KEY!,
  undefined,
  examples,
  undefined,
  {
    parser: {
      provider: "openai",
      openai: {
        baseUrl: "https://api.openai.com/v1",
        model: "gpt-4o-mini",
        statefulValidationRetry: true,
        samplingParams: {
          temperature: 0.2,
          top_p: 1,
          frequency_penalty: 0,
          presence_penalty: 0,
        },
      },
    },
  }
);
```

Provider notes:

- default provider is `groq`
- `GENIE_PARSER_PROVIDER` can select the provider if config is omitted
- OpenAI mode requires an explicit model
- Groq mode defaults to `llama-3.3-70b-versatile`
- base URLs are normalized internally to the expected OpenAI-compatible endpoints

## Monitoring

The NL stack exposes `getLLMMonitor(...)` and integrates with LangSmith-style telemetry.

Monitoring supports:

- prompt / completion token counts
- cost metadata when the backend returns it
- latency tracking
- parser/response errors
- optional telemetry bridges for error and message forwarding

## Public API At a Glance

### Model and decorators

- `GenieObject`
- `DataClass`
- `HelperClass`
- `GenieClass`
- `GenieFunction`
- `GenieProperty`
- `GenieKey`
- `ClassDescriptor`
- `FuncDescriptor`
- `FieldDescriptor`
- `ParamDescriptor`
- `int`
- `float`
- `LazyType`

### Store and registry

- `initGenie`
- `genieDispatch`
- `sharedStore`
- `sharedState`
- `AllGenieObjects`

### DSL execution

- `DslInterpreter`
- `InterpreterError`
- `ClassMissingError`
- `FieldMissingError`
- `FieldStaticError`
- `FunctionMissingError`
- `FunctionStaticError`
- `InvalidParameterTypeError`

### NL parsing and prompting

- `NlInterpreter`
- `NlParser`
- `NlParserGroq`
- `BasicPromptGen`
- `DescriptorPromptGen`
- `ContextAwareDescriptorPromptGen`
- `PromptGen`
- `ExampleParse`
- `PromptSelectionConfig`
- `PromptRuntimeContextSetter`
- `RuntimeUiContext`
- `PromptInteractionRecord`
- `PromptInteractionRecorder`
- `AgentResponsePayload`
- `ResponseSeverity`
- `SamplingParams`

### Monitoring

- `getLLMMonitor`
- `LLMMonitorTelemetryBridge`

## Documentation Split

- `README.md`: install, quick start, feature overview, and public API summary
- `DEVELOPER_GUIDE.md`: deeper package behavior, parser/runtime details, DSL semantics, provider configuration, and troubleshooting

## License

Apache-2.0

This package is a thesis-project fork maintained by [Omkar Mirgal](https://github.com/OmkarMirgal).
For the original ReactGenieDSL project, see [StanfordHCI/ReactGenieDSL](https://github.com/StanfordHCI/ReactGenieDSL).
See the LICENSE file for the full license text.