affordance

A browser for models.

Prompts are not strings. They are rendered.

A language model reads a list of tokens, and it acts by calling tools. Affordance builds that list from an app you write in TSX, and runs your code when the model calls a tool.

Build an appGitHub$cargo install affordance
inbox.tsx
import { Tool } from "@affordance/lib/tool.tsx";

export default function Inbox({ mail }) {
  const [unread, setUnread] = useState(mail);
  /** Archive every message from one sender. */
  function archive({ from }: { from: string }) {
    setUnread(unread.filter(m => m.from !== from));
    return `Archived the mail from ${from}.`;
  }
  return <>
    {`Inbox: ${unread.length} unread\n`}
    {unread.map(m => `${m.from}: ${m.subject}\n`)}
    <Tool fn={archive}>[archive]</Tool>  </>;
}
→
33 tokens, as the model reads them
Inbox: 3 unread⏎
Ada: The build is red⏎
Grace: Lunch on Friday?⏎
Linus: Re: patch v3⏎
[archive]
After the model calls archive with from: "Ada":
Inbox: 2 unread⏎
Grace: Lunch on Friday?⏎
Linus: Re: patch v3⏎
[archive]

A browser turns HTML into pixels, and a person clicks a button. Affordance turns this app into tokens, and a model calls the blue ones, which are the tool archive.

The same parts as the web

A browserAffordance
renders HTML into pixels for a personrenders TSX into tokens for a model
buttons and links a person can clicktools a model can call
CSS, measured in pixelstoken style sheets, measured in tokens
window, with fetch, timers and messagesharness, with models, machines and messages
runs many web apps on one engineruns chat templates and whole agents on one engine

Affordance replaces Jinja chat templates and hand-written agent harnesses with one engine that renders straight to tokens.

v0.1 beta. One Rust binary for Linux and macOS, under Apache-2.0. The app API will change before 1.0.