Documentation

Interactive reference for the Mnemo API. Every code block is copy-paste ready. No auth, no database — just call the endpoints.

Quickstart

Mnemo is a free, database-free SaaS. All endpoints are CORS-enabled and require no authentication. Optionally pass an X-Mnemo-Key header (any string) for tracking — your key is generated client-side and never sent to a database.

health.sh
curl https://forge.ai/api/v1/health
# -> {"status":"ok","service":"forge","version":"2.0.0",...}
mnemo-sdk.ts
import { Forge } from '@forge/sdk'

const forge = new Forge({ apiKey: process.env.FORGE_KEY! })

// Memorizer
const { codes } = await forge.memorizer.compress({
  text: 'User prefers dark mode and TypeScript.',
})
const { text } = await forge.memorizer.decompress({ codes })
console.assert(text === 'User prefers dark mode and TypeScript.')

// Orchestrator
const result = await forge.orchestrator.execute({
  messages: [{ role: 'user', content: 'Calculate 5 + 2' }],
  tools: [{
    type: 'function',
    function: {
      name: 'calc',
      description: 'Evaluate a math expression',
      parameters: {
        type: 'object',
        properties: { expr: { type: 'string' } },
        required: ['expr'],
      },
    },
  }],
})

if (result.tool_calls) {
  for (const call of result.tool_calls) {
    console.log(call.function.name, call.function.arguments)
  }
}

Don't have an SDK yet? All endpoints speak plain JSON. See the cURL examples below for each route.

Memorizer

The Memorizer-LM model compresses text into a dynamic number of integer codes — from 1 code for small inputs up to a maximum of 20 codes for 1TB+ of text. The 10-code threshold is reached only at 600MB of input. Decompression always returns the exact original text. Under the hood it uses a 2-layer Transformer encoder with a Gumbel-Softmax discrete bottleneck and a non-autoregressive decoder.

Code Scaling Table

< 1KB → 1 code
< 1MB → 4 codes
< 100MB → 6 codes
< 600MB → 9 codes
600MB–1GB10 codes
< 10GB → 14 codes
< 1TB → 18 codes
≥ 1TB → 20 codes (max)

POST /compress

compress.sh
curl -X POST https://forge.ai/api/v1/memorizer/compress \
  -H "Content-Type: application/json" \
  -d '{"text":"The user prefers dark mode and TypeScript."}'

# -> {"codes":[3719483],"codes_out":1,"max_codes":20,"bytes_in":48,"ratio":0.083}
# 1 code for small text, scales up to 20 codes for 1TB+ inputs

POST /decompress

decompress.sh
curl -X POST https://forge.ai/api/v1/memorizer/decompress \
  -H "Content-Type: application/json" \
  -d '{"codes":[3719483]}'

# -> {"text":"The user prefers dark mode and TypeScript.","bytes_out":48}

Request: { "text": string } / { "codes": [n1, n2, ..., n20] }

Response: { "codes": [...], "codes_out": N, "max_codes": 20 } / { "text": string }

SML — Memory Retrieval

SML (Structured Memory Loader) is not a chat model. It is a pure memory retrieval system. Given a codes array from the Memorizer, SML decompresses the memory and returns it as a structured array of parts (headings, paragraphs, list items, code blocks, tables, images, links, quotes, symbols). No question-answering, no summarization — just retrieval.

POST /api/v1/memory/chat

sml-retrieve.sh
# 1. Compress text into codes
curl -X POST https://forge.ai/api/v1/memorizer/compress \
  -H "Content-Type: application/json" \
  -d '{"text":"# Title\n\nE = mc² where π ≈ 3.14159.\n\n- Item 1\n- Item 2"}'

# -> {"codes":[1728893659],"pct_smaller":85.2,...}

# 2. Retrieve the memory as structured parts
curl -X POST https://forge.ai/api/v1/memory/chat \
  -H "Content-Type: application/json" \
  -d '{"codes":[1728893659]}'

# -> {
#   "parts": [
#     { "type": "heading", "content": "Title", "level": 1 },
#     { "type": "paragraph", "content": "E = mc² where π ≈ 3.14159." },
#     { "type": "symbol", "content": "²" },
#     { "type": "symbol", "content": "π" },
#     { "type": "symbol", "content": "≈" },
#     { "type": "list_item", "content": "Item 1" },
#     { "type": "list_item", "content": "Item 2" }
#   ],
#   "memory_chars": 52,
#   "memory_bytes": 55,
#   "memory_text": "the full decompressed text",
#   "part_count": 7
# }

Part Types

  • heading — H1–H6 (with level)
  • paragraph — plain text block
  • list_item — list entry
  • code_block — fenced code (with lang)
  • table — Markdown table
  • image — ![alt](url)
  • link — [text](url)
  • quote — blockquote
  • symbol — Unicode symbol/formula

Supported Symbols

  • • Math: ∑ ∫ √ ∞ ≈ ≠ ≤ ≥ ± × ÷ π Δ
  • • Arrows: → ← ↑ ↓ ⇒ ⇔
  • • Sub/superscript: ² ³ ₁ ₂ ½
  • • Currency: $ € £ ¥ ₹ ¢
  • • Greek: α β γ δ θ λ μ σ φ ω
  • • Emoji & pictographs
  • • CJK characters

HTML→MD Nano

HTML→MD is a small, fast expert model that does one thing: converts raw HTML to clean, LLM-ready Markdown with the exact same content. It strips noise (scripts, nav, share buttons, comments, related posts, author bios) and preserves headings, paragraphs, lists, links, images, tables, and code blocks. No content is added or removed — just reformatted.

POST /api/v1/html2md/convert

html2md.sh
# Convert raw HTML to Markdown
curl -X POST https://forge.ai/api/v1/html2md/convert \
  -H "Content-Type: application/json" \
  -d '{"html":"<article><h1>Title</h1><p>Content with <a href=\"https://x.com\">link</a>.</p></article>"}'

# -> {
#   "markdown": "# Title\n\nContent with [link](https://x.com).",
#   "title": "Title",
#   "byline": null,
#   "site_name": ""
# }

# Or fetch a URL and convert
curl -X POST https://forge.ai/api/v1/html2md/convert \
  -H "Content-Type: application/json" \
  -d '{"url":"https://example.com/article"}'

What gets stripped

  • • Scripts, styles, nav, footer, header
  • • Share buttons (Twitter, Facebook, Reddit, etc.)
  • • Comments and reply forms
  • • Related posts / recommended articles
  • • Author bios and contact info
  • • Newsletters, ads, popups, sidebars
  • • Excessive blank lines (max 2 consecutive)

Supported HTML Elements

  • Headings: h1–h6 → # to ######
  • Text formatting: strong/b → **bold**, em/i → *italic*
  • Strikethrough: del/s/strike → ~~text~~
  • Insert: ins → text (underline not in MD)
  • Highlight: mark → **bold**
  • Subscript: sub → ~text~
  • Superscript: sup → ^text^
  • Inline code: code, kbd, samp → `text`
  • Variable: var → *text*
  • Citation: cite, dfn → *text*
  • Quote: q → "text"
  • Abbreviation: abbr[title] → text (title)
  • Address: address → *text*
  • Code blocks: pre[code] → ```lang ... ```
  • Lists: ul → - items, ol → 1. items
  • Definition lists: dl/dt/dd → **term** / : def
  • Tables: table/thead/tbody/tr/th/td → MD tables
  • Blockquotes: blockquote → > text
  • Details/summary: details → **summary** + content
  • Links: a[href] → [text](url)
  • Images: img[src,alt] → ![alt](url)
  • Horizontal rule: hr → ---
  • Line break: br → newline
  • Paragraphs: p → text blocks
  • HTML entities: & → &, π → π, ° → °, etc.

Orchestrator

The Orchestrator-Nano model is an 8-layer decoder (768 hidden, 12 heads, GQA with 4 KV heads, RMSNorm, SiLU) trained to emit strictly typed JSON. It always returns an object with content and tool_calls fields. Maximum 200 output tokens.

POST /api/v1/orchestrator/execute

orchestrator.sh
# The orchestrator solves math/percentage queries directly — no tool needed
curl -X POST https://forge.ai/api/v1/orchestrator/execute \
  -H "Content-Type: application/json" \
  -d '{
    "messages":[
      {"role":"user","content":"1365 students, 367 failed, what % passed and failed?"}
    ]
  }'

# -> {"content":"Out of 1365 students: 998 passed (73.11%) and 367 failed (26.89%).","tool_calls":null}

# Or supply tools — the orchestrator routes to the best match
curl -X POST https://forge.ai/api/v1/orchestrator/execute \
  -H "Content-Type: application/json" \
  -d '{
    "messages":[{"role":"user","content":"Calculate 5 + 2"}],
    "tools":[{
      "type":"function",
      "function":{
        "name":"calc",
        "description":"Evaluate a math expression",
        "parameters":{
          "type":"object",
          "properties":{"expr":{"type":"string"}},
          "required":["expr"]
        }
      }
    }]
  }'

# -> {"content":null,"tool_calls":[{"id":"call_1","type":"function",
#     "function":{"name":"calc","arguments":"{\"expr\":\"5 + 2 = 7\"}"}}]}

Tool call: {"content":null,"tool_calls":[{"id":"call_1","type":"function","function":{"name":"calc","arguments":"{\"expr\":\"5+2\"}"}}]}

Final answer: {"content":"7","tool_calls":null}

Streaming (SSE)

For progressive JSON rendering, use the streaming endpoint. Tokens arrive as Server-Sent Events; the final data: [DONE] marks the end of the stream.

stream.ts
const res = await fetch('/api/v1/orchestrator/execute-stream', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ messages, tools }),
})

const reader = res.body!.getReader()
const decoder = new TextDecoder()
let buffer = ''
let json = ''

while (true) {
  const { done, value } = await reader.read()
  if (done) break
  buffer += decoder.decode(value, { stream: true })
  const events = buffer.split('\n\n')
  buffer = events.pop() || ''
  for (const ev of events) {
    if (!ev.startsWith('data: ')) continue
    const data = ev.slice(6)
    if (data === '[DONE]') return
    const { token } = JSON.parse(data)
    json += token
    console.log('partial:', json)
  }
}

Integrations

Mnemo is a drop-in replacement. Keep your existing provider for final answers; let Mnemo handle memory compression and tool-call routing.

Vercel AI SDK (Next.js 16)

app/api/chat/route.ts
// app/api/chat/route.ts
import { streamText, convertToCoreMessages } from 'ai'
import { mnemo } from '@forge/sdk'

export async function POST(req: Request) {
  const { messages, tools } = await req.json()

  // 1. Compress prior turns (lossless) before sending to the LLM.
  const compressed = await forge.memory.compress(messages)

  // 2. Let Mnemo's on-device orchestrator decide tool calls.
  const decision = await forge.orchestrator.execute({
    messages: compressed,
    tools,
  })

  // 3. Stream a final answer using your existing provider.
  const result = await streamText({
    model: yourProvider, // openai('gpt-4o'), anthropic('claude-3-5-sonnet'), etc.
    messages: convertToCoreMessages(compressed),
    tools,
  })

  return result.toDataStreamResponse()
}

LangChain

langchain_agent.py
from langchain.agents import AgentExecutor, create_tool_calling_agent
from langchain_core.prompts import ChatPromptTemplate
from forge import ForgeOrchestrator, ForgeMemory

llm = ForgeOrchestrator(model="nano-q4")    # on-device JSON-only
memory = ForgeMemory()                       # lossless 4-code compression

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful assistant."),
    ("placeholder", "{chat_history}"),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_tool_calling_agent(llm, tools, prompt)
executor = AgentExecutor(agent=agent, tools=tools, memory=memory, verbose=True)

await executor.ainvoke({"input": "What's 23 * 17?"})

LangGraph

langgraph_graph.py
from langgraph.graph import StateGraph, END
from forge import ForgeMemory, ForgeOrchestrator

memory = ForgeMemory()
orch  = ForgeOrchestrator(model="nano-q4")

def call_model(state):
    state["messages"] = memory.compress(state["messages"])
    decision = orch.execute(state["messages"], state["tools"])
    if decision.tool_calls:
        state["pending_calls"] = decision.tool_calls
    else:
        state["final"] = decision.content
    return state

graph = StateGraph(dict)
graph.add_node("model", call_model)
graph.set_entry_point("model")
graph.add_edge("model", END)
app = graph.compile()

Python SDK

mnemo.py
from forge import Forge

forge = Forge(api_key=os.environ['FORGE_KEY'])

# Memorizer
codes = forge.memorizer.compress(text='User prefers dark mode.').codes
text = forge.memorizer.decompress(codes=codes).text
assert text == 'User prefers dark mode.'

# Orchestrator
result = forge.orchestrator.execute(
    messages=[{'role': 'user', 'content': 'Calculate 5 + 2'}],
    tools=[{
        'type': 'function',
        'function': {
            'name': 'calc',
            'description': 'Evaluate a math expression',
            'parameters': {
                'type': 'object',
                'properties': {'expr': {'type': 'string'}},
                'required': ['expr'],
            },
        },
    }],
)

if result.tool_calls:
    for call in result.tool_calls:
        print(call.function.name, call.function.arguments)

On-Device Deployment

The same models can run client-side. Use the export scripts in /export to produce ONNX int8, TensorFlow Lite int8, or gguf q4 artifacts.

Browser (onnxruntime-web)

browser.ts
// Load Mnemo Orchestrator in the browser
import * as ort from 'onnxruntime-web'

const session = await ort.InferenceSession.create('/models/orchestrator_int8.onnx')

export async function runOnDevice(messages, tools) {
  const inputIds = tokenize(buildPrompt(messages, tools))
  const feeds = { input_ids: new ort.Tensor('int32', inputIds, [1, inputIds.length]) }
  const out = await session.run(feeds)
  return parseJsonTokens(out.logits)
}

Mobile (TensorFlow Lite)

mobile.ts
// React Native / Expo
import * as tf from '@tensorflow/tfjs'
import '@tensorflow/tfjs-react-native'

const model = await tf.loadGraphModel(
  'https://cdn.forge.ai/models/memorizer_int8.tflite'
)

export function compressOnDevice(text) {
  const ids = tokenize(text)
  const out = model.predict(tf.tensor2d([ids])) as tf.Tensor
  return Array.from(out.dataSync()).slice(0, 4) // 4 codes
}

Desktop / Edge (llama.cpp + gguf)

llama-cpp.sh
// llama.cpp server
./server -m orchestrator_q4.gguf --port 8080 --json-schema \
  '{"type":"object","properties":{"content":{"type":["string","null"]},"tool_calls":{"type":["array","null"]}}}' \
  --max-tokens 200

# Client (any OpenAI-compatible client)
curl http://localhost:8080/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"model":"orchestrator","messages":[...]}'

Rate Limits & CORS

Every endpoint is rate-limited to 100 requests / minute per IP, tracked in-memory. The free tier has no daily cap. All responses include permissive CORS headers, so you can call Mnemo directly from the browser.

  • Access-Control-Allow-Origin: *
  • Methods: GET, POST, OPTIONS
  • Headers: Content-Type, Authorization, X-Mnemo-Key, X-Client-Id
  • 429 response includes retry_after (seconds)

No data is ever stored.

Rate-limit buckets and the in-process memorizer cache live only for the lifetime of a single cold start. Cold-start resets are normal on the free tier.

Read the privacy policy