# How Model Behavior Is Configured Within OpenWork: A Layered LLM Configuration System

> Discover how OpenWork configures model behavior using a layered LLM system. Learn about global defaults, workspace variants, and session overrides for consistent AI interactions.

- Repository: [Different AI/openwork](https://github.com/different-ai/openwork)
- Tags: deep-dive
- Published: 2026-08-06

---

**OpenWork configures model behavior through a layered system that combines global defaults, workspace-specific variants, and session-level overrides, with normalization logic ensuring consistent variant mapping across the application.**

The `different-ai/openwork` repository implements a sophisticated configuration architecture for controlling Large Language Model (LLM) behavior. This system allows users to specify not only which model provider to use (OpenAI, Anthropic, Google) but also how that model should operate through cost-versus-quality variants. Understanding how model behavior is configured within OpenWork requires examining five distinct configuration layers that work together to provide flexibility at the global, workspace, and session levels.

## The Five Layers of Model Configuration

OpenWork's configuration hierarchy ensures that user preferences propagate correctly from global defaults down to individual chat sessions. Each layer serves a specific purpose in the decision chain.

### Global Default Model

The foundation of the configuration system is the **default model**, which acts as a global fallback when no specific overrides exist. This value is persisted in the browser's `localStorage` under the key defined by `MODEL_PREF_KEY`.

In [`apps/app/src/react-app/kernel/model-config.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/kernel/model-config.ts), the `readStoredDefaultModel` function retrieves this value with proper error handling and fallback logic:

```typescript
export function readStoredDefaultModel(): ModelRef {
  if (typeof window === "undefined") return DEFAULT_MODEL;
  try {
    const stored = window.localStorage.getItem(MODEL_PREF_KEY);
    return parseModelRef(stored) ?? DEFAULT_MODEL;
  } catch {
    return DEFAULT_MODEL;
  }
}

```

When users select a new default through the UI, `writeStoredDefaultModel` updates storage and dispatches a custom `openwork.defaultModelChanged` event. This allows reactive components to update immediately without requiring a page refresh.

### Workspace Model Variants

Workspaces can define **model variants** that specify quality or reasoning depth (e.g., `medium`, `high`) for their default models. This configuration is stored under a namespaced key combining `VARIANT_PREF_KEY` with the specific `workspaceId`.

The `parseWorkspaceModelVariants` function in [`model-config.ts`](https://github.com/different-ai/openwork/blob/main/model-config.ts) handles parsing and normalization:

```typescript
export function parseWorkspaceModelVariants(
  raw: string | null,
  fallbackModel: ModelRef = DEFAULT_MODEL,
): Record<string, string> {
  // …JSON‑parse + normalizeModelBehaviorValue…
}

```

If parsing fails, the system falls back to a normalized variant appropriate for the fallback model, ensuring workspaces always have valid configuration even when data corruption occurs.

### Session-Level Overrides

Individual chat sessions may temporarily override both the model and variant selections. These **session overrides** take precedence over workspace and global defaults, allowing users to experiment with different configurations without changing their persistent preferences.

The system stores these as a JSON map where each session ID maps to a `SessionChoiceOverride` object containing optional `model` and `variant` properties:

```typescript
export function parseSessionChoiceOverrides(
  raw: string | null,
): Record<string, SessionChoiceOverride> {
  // …JSON‑parse → per‑session model/variant extraction…
}

```

The companion function `serializeSessionChoiceOverrides` handles the reverse operation when persisting changes back to storage.

### Normalization and Sanitization

User input from dropdowns or imported configurations requires **normalization** to ensure consistency. The `normalizeModelBehaviorValue` function in [`apps/app/src/app/lib/model-behavior.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/app/lib/model-behavior.ts) treats synonyms like "balance", "balanced", "default", and "provider-default" as **null** values, indicating the system should use the provider's native default behavior.

```typescript
export const normalizeModelBehaviorValue = (value: string | null) => {
  if (!value) return null;
  const normalized = value.trim().toLowerCase();
  if (["balance","balanced","default","provider-default"].includes(normalized)) {
    return null;
  }
  return normalized;
};

```

This normalization prevents legacy values (such as the deprecated "balance" variant) from causing runtime errors while allowing users to express their intent through multiple common terms.

### Variant Metadata and UI Presentation

The final layer provides **human-readable metadata** for each variant. The `getModelBehaviorOptions` function inspects a provider's `ProviderModel` definition to discover available variants, sorts them using a well-known priority order, and returns structured data for UI rendering.

```typescript
export const getModelBehaviorOptions = (
  providerID: string,
  model: ProviderModel,
  providerName?: string | null,
): ModelBehaviorOption[] => {
  const variantKeys = sortVariantKeys(getVariantKeys(model));
  return variantKeys.map((key) => ({
    value: key,
    label: getVariantLabel(key),
    description: getVariantDescription(providerID, key, getVariantLabel(key), providerName),
  }));
};

```

Descriptions are provider-specific and sourced from localization keys (e.g., `t("model_behavior.desc_…")`), ensuring users see relevant explanations for Anthropic's Claude variants versus OpenAI's GPT models. The `getModelBehaviorSummary` function combines these elements into concise UI cards that display the current configuration state.

## Core Implementation Files

Understanding the file structure helps developers navigate the configuration system:

- **[`apps/app/src/react-app/kernel/model-config.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/kernel/model-config.ts)** – Core storage, parsing, and serialization logic for defaults, workspace variants, and session overrides.
- **[`apps/app/src/app/lib/model-behavior.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/app/lib/model-behavior.ts)** – Normalization rules, variant discovery, and UI-friendly label generation.
- **[`apps/app/src/react-app/domains/settings/state/model-controls-store.ts`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/domains/settings/state/model-controls-store.ts)** – Zustand store managing reactive state for model and variant selections.
- **[`apps/app/src/components/model-select.tsx`](https://github.com/different-ai/openwork/blob/main/apps/app/src/components/model-select.tsx)** – Dropdown component for model selection.
- **[`apps/app/src/components/model-behavior-select.tsx`](https://github.com/different-ai/openwork/blob/main/apps/app/src/components/model-behavior-select.tsx)** – Dropdown component for variant/behavior selection.
- **[`apps/app/src/react-app/domains/session/modals/model-picker-modal.tsx`](https://github.com/different-ai/openwork/blob/main/apps/app/src/react-app/domains/session/modals/model-picker-modal.tsx)** – Modal interface combining both selectors for user configuration.

## Practical Configuration Examples

Reading the current default model in a React component:

```typescript
import { readStoredDefaultModel } from '@/react-app/kernel/model-config';

const defaultModel = readStoredDefaultModel(); 
// → { providerID: 'openai', modelID: 'gpt-4' }

```

Retrieving available behavior options for a specific provider model:

```typescript
import { getModelBehaviorOptions } from '@/app/lib/model-behavior';
import type { ProviderModel } from '@/app/types';

const options = getModelBehaviorOptions('anthropic', myModel as ProviderModel);
// options = [{ value: 'low', label: 'Low', description: '...' }, …]

```

Persisting session-specific overrides:

```typescript
import { serializeSessionChoiceOverrides } from '@/react-app/kernel/model-config';

const overrides = {
  'session-123': { 
    model: { providerID: 'anthropic', modelID: 'claude-2' }, 
    variant: 'high' 
  },
};
const json = serializeSessionChoiceOverrides(overrides);
// json → '{"session-123":{"model":"anthropic/claude-2","variant":"high"}}'

```

Using the reactive hook for default model management:

```tsx
import { useDefaultModel } from '@/react-app/kernel/model-config';

function ModelSwitcher() {
  const [model, setModel] = useDefaultModel();

  return (
    <select value={model.modelID} onChange={e => setModel({ ...model, modelID: e.target.value })}>
      {/* options built from provider list */}
    </select>
  );
}

```

## Summary

- **Global defaults** provide the base configuration via `localStorage` and `readStoredDefaultModel`, ensuring the application always has a valid fallback model.
- **Workspace variants** allow teams to specify quality preferences (low, medium, high) per workspace using `parseWorkspaceModelVariants` and namespaced storage keys.
- **Session overrides** enable temporary experimentation through `parseSessionChoiceOverrides`, which takes precedence over all other configuration layers.
- **Normalization logic** in `normalizeModelBehaviorValue` sanitizes legacy values and synonyms to prevent configuration errors.
- **Metadata generation** via `getModelBehaviorOptions` creates provider-specific UI labels and descriptions that adapt to Anthropic, OpenAI, and Google model specifications.

## Frequently Asked Questions

### How does OpenWork handle invalid or legacy model behavior values?

OpenWork sanitizes user input through the `normalizeModelBehaviorValue` function in [`model-behavior.ts`](https://github.com/different-ai/openwork/blob/main/model-behavior.ts). Values like "balance", "balanced", "default", or "provider-default" are normalized to **null**, which signals the system to use the provider's native default behavior. This prevents runtime errors from deprecated configuration values while maintaining backward compatibility.

### Can different workspaces use different model quality settings?

Yes. OpenWork supports **workspace model variants** through the `parseWorkspaceModelVariants` function. Each workspace can store a specific variant (e.g., `high` for maximum reasoning depth or `low` for cost savings) under a unique `localStorage` key constructed from `VARIANT_PREF_KEY` and the workspace ID. These settings apply only within that workspace context.

### What happens when a user selects a different model for a single chat session?

The system creates a **session override** using `parseSessionChoiceOverrides`. This stores a temporary mapping of session IDs to specific model and variant choices. Session overrides take precedence over both workspace variants and global defaults, but they do not modify the persisted default configuration. When the session ends or the user clears overrides, the system reverts to the workspace or global default.

### Where is the model configuration state managed in the React frontend?

The configuration state is managed by **Zustand stores** in [`model-controls-store.ts`](https://github.com/different-ai/openwork/blob/main/model-controls-store.ts), with persistence handled by the functions in [`model-config.ts`](https://github.com/different-ai/openwork/blob/main/model-config.ts). UI components like [`model-picker-modal.tsx`](https://github.com/different-ai/openwork/blob/main/model-picker-modal.tsx) and [`model-behavior-select.tsx`](https://github.com/different-ai/openwork/blob/main/model-behavior-select.tsx) consume this state through hooks such as `useDefaultModel`, ensuring reactive updates across the application when configuration changes occur.