Skip to Content
Overview

Overview

Rosetta is a clinical writing app in the browser. You draft structured notes in a template-aware editor. Alongside it, an AI assistant answers questions against your own reference material, proposes edits you review before they apply, and calls clinical tools such as terminology lookups and score calculators.

These docs describe the current Rosetta application.

The Workspace

The screen has three regions.

RegionContents
Left sidebarProjects, Templates, Smart Phrases, and Slash Commands
CenterThe note editor, with a tab strip showing open notes, the autosave timestamp, save history, and the template menu
Right sidebarRevisions, Workflows, RAG sources, and Reasoning. This is where AI output lands for review

Projects group your notes and carry a specialty and care context that shape AI behavior for every note inside them.

Main Areas

Editor

The editor is built on Lexical. Notes stay readable as plain text, and Rosetta adds nodes for template anchors, fields, and suggestion previews. Agent edits arrive as pending suggestions you accept or reject, and locked template sections can’t be modified by agents at all.

See the Editor section for the document model, lock behavior, and suggestion flow.

Workflows and Agents

Workflows are preset note actions such as summaries, Assessment & Plan drafts, de-identification, red flag checks, and presentations. They run from the right sidebar and return output to chat. Selecting text and right-clicking runs an agent on just that selection.

The assistant can also call clinical tools during a chat turn: ICD-10, LOINC, and RxNorm lookups, PubMed search, OpenFDA and clinical trial queries, and clinical score calculators.

See Workflows for workflow and agent behavior.

RAG

Rosetta can use uploaded files, pasted text, saved account sources, and PubMed results. The retriever pulls sources into an answer and formats citations as [Source N].

See RAG for retrieval behavior.

Bring Your Own Key

Rosetta talks directly to model providers with keys you supply in Settings. Google (Gemini), OpenAI, Anthropic, DeepSeek, and OpenRouter are supported, and Tavily can be added for live web search. Every key is optional. Features that depend on a provider you haven’t configured won’t run.

Getting Started

Add a provider key, open a note, and ask the assistant for something. The whole setup takes about two minutes.

Prerequisites

  • A current version of Chrome, Firefox, Safari, or Edge.
  • An API key for at least one supported provider. A Google Gemini key covers the default model path and retrieval.

First Time Setup

1. Add a Provider Key

Open Settings → AI Models and paste a key for the provider you use:

Google (Gemini) AIza... OpenAI sk-... Anthropic sk-ant-... DeepSeek sk-... OpenRouter sk-or-...

Keys can be saved or removed per provider. Without a key the editor still works, but AI features will not run. OpenRouter gives you access to models the other four providers don’t offer.

2. Add a Tavily Key for Web Search (Optional)

Rosetta uses Tavily for live web search when the assistant needs current information that is not in your sources or PubMed. Add the key under Settings → Tavily web search. You can also provide it through the VITE_TAVILY_API_KEY environment variable for local development. Without a Tavily key the rest of the app works normally, and the assistant skips web search.

3. Sign In (Optional)

Signing in syncs your settings, scheduled tasks, and workspace records across browsers. Signed out, everything stays in the browser’s local storage on that machine. Both modes work; sign in when you want your workspace available on more than one machine.

4. Learn the Interface

  • Left sidebar: Projects, Templates, Smart Phrases, and Slash Commands.
  • Center: the note editor, with a tab strip showing open notes, the autosave timestamp, save history, and the template menu.
  • Right sidebar: Revisions, Workflows, RAG sources, and Reasoning.

Basic Workflow

  1. Create a project, or open one. Each project carries a specialty and a care context (Internal Medicine and Outpatient by default) that shape AI output for its notes.
  2. Open a note and apply a template from the template menu if you want a fixed structure.
  3. Write. Autosave runs continuously, and the timestamp in the tab strip shows the last save.
  4. Add reference material in the RAG panel so answers can cite your own sources.
  5. Ask the assistant in chat, or select text and right-click to run an agent on just that selection.
  6. Review each pending suggestion in the editor and accept or reject it. Nothing changes your note until you accept it.

For a worked example of steps 4 through 6, see First Document.

Synthetic Cases

You do not need a real chart to try Rosetta. Ask the assistant in chat to create synthetic cases and it will draft the notes and store them as a project:

Create a project of 5 synthetic inpatient cases for internal medicine, each with an H&P and a progress note.

Keyboard Shortcuts

ShortcutAction
Ctrl/Cmd + SSave the current note, or the template in the template editor
Ctrl/Cmd + WClose the current note tab
EscClose the settings panel
/Slash commands in the editor

Smart phrases expand as you type. For example:

.hpi → History of Present Illness:

Press Enter or Tab to accept the expansion, or Esc to dismiss it.

If Something Goes Wrong

Most problems trace back to a missing key or an excluded source. See Troubleshooting for the common errors and their fixes.

First Document

Three things you’ll use on most notes: save history, right-click actions on selected text, and chat with citations.

Save History

Autosave runs continuously while you type, and the timestamp in the note tab strip shows the last save. Manual saves (Ctrl/Cmd + S or the save button in the tab strip) create named snapshots. Open the history menu in the tab strip to restore, rename, or delete a snapshot. Restoring does not discard your current draft’s history, so you can compare freely.

Right-Click Actions

Select text and right-click to run an agent on just that selection: expand shorthand, reformat, reason through it, or pull citations. The agent returns a pending suggestion. Your original text stays until you accept or reject it, and suggestions that touch locked template sections are dropped.

For example, selecting this and running the Shorthander:

55yo M c CP, SOB x2d

produces a pending suggestion like:

55-year-old male with chest pain and shortness of breath for two days

RAG Workflow

Add a source in the RAG panel first. Click Add Source, give it a name, and paste or upload content:

Name: Sepsis Guidelines 2024 Content: - Draw blood cultures before antibiotics - Start broad-spectrum coverage within 1 hour - Piperacillin-tazobactam 4.5 g IV q6h - Add vancomycin if MRSA risk factors

Each source has an “included in ingest” toggle, and only included sources are searched.

Then ask the assistant a clinical question in chat:

What antibiotics should I start for suspected sepsis?

It retrieves the relevant passages from your included sources (and PubMed, if you use the PubMed search), answers with [Source N] citations inline, and lists the cited sources under the response. If an answer comes back without citations you expected, check that the source has usable text and is still marked as included.

Troubleshooting

Most problems come down to a missing key, an excluded source, or a file Rosetta could not read. This page lists the errors you are most likely to hit and what to do about each one.

No AI API Key Configured

No AI API key configured. Add one in Settings.

The assistant has no provider key to work with. Open Settings → AI Models and add a key for Google, OpenAI, Anthropic, DeepSeek, or OpenRouter. A Google Gemini key is the simplest starting point and covers both the default model and retrieval.

Tavily API Key Is Missing

Tavily API key is missing. Set it in Settings or VITE_TAVILY_API_KEY.

This appears only when the assistant tries to run a web search and no Tavily key is set. Add one under Settings → Tavily web search, or set VITE_TAVILY_API_KEY for local development. Web search is optional, so you can also ignore this and rely on your own sources and PubMed.

Empty or Failed Responses

A request that returns nothing usually means the provider key is missing, invalid, or out of quota. Check the key in Settings first. If the key is valid, the provider itself may be rate limiting or temporarily unavailable, in which case waiting and retrying is the fix.

Answers Ignore Your Sources

If the assistant answers without using material you added, the source is almost always empty or excluded. Open the RAG panel and confirm two things:

  1. The source has content.
  2. Its “included in ingest” toggle is on.

Only included sources with usable text are searched.

PDF Text Extraction Failed

[PDF uploaded - automatic text extraction failed. Convert to text before ingesting.]

Rosetta parses PDFs in the browser, but scanned or image-only PDFs have no selectable text to extract. The source is still stored, but it will not enter retrieval. Convert the document to text, or paste the relevant passages in as a text source instead.

A Revision Skipped Part of Your Request

On a note with a locked template, suggested changes that conflict with a locked section anchor are dropped. This protects required headings, disclaimers, and billing fields. Unlock the template or rephrase the request so it applies to an editable section.

Settings or Tasks Are Not Syncing

Sync requires an account. Signed out, your workspace lives only in the browser’s local storage on the current machine, and scheduled tasks will not run in the background. Sign in if you want settings and tasks to persist across browsers and sessions.

The App Will Not Start Locally

Install dependencies and start the dev server from the project folder:

cd rosetta npm install npm run dev

The dev server expects its port to be free. If another process is using it, stop that process or start Rosetta on a different port.

Frequently Asked Questions

What Is Rosetta?

Rosetta is a clinical text editor with built-in agents. You write notes as usual. Agents do the tedious work: expanding abbreviations, pulling references, formatting citations, and drafting reasoning. Every change an agent proposes has to be accepted before it modifies your document.

How Do I Get Access?

Sign in at philipshih.org/apps/rosetta . Account creation is limited during the beta.

Which AI Models Does Rosetta Use?

Rosetta is a bring-your-own-key service. You add provider keys in Settings → AI Models, and Rosetta uses them for supported AI features. Google (Gemini), OpenAI, Anthropic, DeepSeek, and OpenRouter are supported. OpenRouter gives you access to models the other four don’t offer, and a Gemini key covers the default model path and retrieval. Web search uses a separate Tavily key, set under Settings → Tavily web search.

Where Does My Data Live?

Signed out, everything stays in the browser’s local storage, so clearing site data clears your workspace. Signed in, settings, scheduled tasks, and durable workspace records sync to your account, and notes are also cached locally so the app stays responsive.

Prompts, retrieved source passages, and the note content involved in a request are sent to whichever model provider handles that request.

What Can I Upload Into RAG?

The RAG panel accepts PDFs, plain text, Markdown, JSON, CSV, TSV, HTML, RTF, and common image formats. You can also search PubMed in the app.

What Is The Difference Between Plan And Act?

Plan reasons through a request first. Act returns an answer, draft, or edit right away.

How Do Templates Work?

Rosetta supports shared templates, workspace defaults, and local overrides for one note. A note with a template applied is locked to that structure: section anchors constrain AI edits, and locked anchors can’t be modified by agents at all.

How Do I Use Chat Tools?

Ask a free-form question in the chat panel, or select text and right-click to run an agent on just that selection: expand shorthand, reformat, reason, or cite. In both cases the result comes back as a pending suggestion you accept or reject.

What Is Auto-Approve?

By default, agent actions that change your workspace pause for your approval. Settings has an “Auto-approve agent actions” toggle that skips the confirmation step. Leave it off until you trust how agents behave on your own material.

What Happens When Rosetta Suggests An Edit?

Rosetta stores the edit as a suggestion first. You review it in the editor and accept or reject it. Only accepted suggestions enter revision history.

What Appears In Revisions?

The Revisions tab shows accepted AI changes. Each entry records the original text, the accepted text, the reason, and the time accepted.

Why Did A Revision Skip Part Of My Request?

On notes with a locked template, suggested changes that conflict with locked section anchors are dropped. Unlock the template or rephrase the request so it applies to an editable section.

Can Rosetta Search PubMed?

Yes. The RAG panel can query PubMed through the NCBI E utilities APIs and add selected articles as sources.

Does Rosetta Support General Document Import And Export?

Yes. Paste or upload raw clinical text and Rosetta segments it into projects and notes, splitting cases on your configured delimiter or letting the model segment them. For export, a folder’s notes can be downloaded as a single text file, and RAG sources can be downloaded individually.


Questions about the current docs can be sent to philip@philipshih.org.

Editor

The editor is built on Lexical 0.39. A note is stored as a tree of editor nodes. Agent edits appear as pending suggestions first. You accept or reject each suggestion before it changes the note. Template sections can be locked so agents cannot write to them.

Components

AreaWhat it does
EditorCoreMounts Lexical, tracks selection, and dispatches commands.
LexicalNoteEditorThe active editor runtime: sets up the composer, owns the live Lexical editor instance, and bridges debounced snapshots back to app state. NoteEditorV2 remains as a compatibility wrapper around it.
TemplateManagerHolds template anchors and fields. Blocks writes to locked regions.
SuggestionOverlayRenders pending agent diffs in the editor and routes review to the revisions surface.
SyncBridgePersists debounced plain text and Lexical JSON snapshots, with blur and unload flushes.

Data Flow

User input enters EditorCore, updates the Lexical state, and emits change pipeline events. The pipeline updates undo/redo history, the operational change log, and the agent suggestion queue. The log feeds local storage and sync snapshots, while queued suggestions render in the overlay until the user accepts or rejects them.

Core Rules

  1. All document mutations go through editor.update(). Every write is transactional and observable.
  2. Lexical is the live typing source of truth. React note state and persisted JSON are snapshots.
  3. Agents and automation cannot modify locked template anchors. Only a user edit can.
  4. Each suggestion is tagged with the revision it was generated against. If the user edits in between, the suggestion’s range is remapped onto the new revision, or marked stale if remapping can’t resolve it.
  5. Undo/redo only covers changes that actually landed. Rejected and stale suggestions never wrote to the document, so they don’t appear in history.
  6. Every applied change, whether from a user or an agent, emits the same change event. Sync, logging, and other subscribers read from one stream.

Document Model

Documents are serialized as node trees:

interface SerializedEditorState { root: { type: "root"; version: number; children: SerializedNode[]; direction: "ltr" | "rtl" | null; }; } interface SerializedNode { type: string; version: number; children?: SerializedNode[]; text?: string; format?: number; detail?: number; mode?: "normal" | "segmented" | "token"; }

Node Types

class RosettaTextNode extends TextNode { __format: number; } class RosettaParagraphNode extends ParagraphNode { __indent: number; } class AnchorNode extends ElementNode { __label: string; __isLocked: boolean; } class FieldNode extends DecoratorNode<JSX.Element> { __fieldType: "SELECT" | "MULTISELECT" | "DYNAMIC_SELECT"; __options: string[]; }

Change Tracking

Rosetta keeps two separate records of changes:

  • Lexical history stack: what undo/redo walks through. Only contains changes that were applied.
  • Operational log: what sync and agent bookkeeping read from. Contains every transaction with its source, revision, and ranges.

Transaction Shape

type ChangeSource = "user" | "ai" | "template" | "sync"; type ChangeKind = "insert" | "replace" | "delete" | "format"; interface ChangeTransaction { id: string; revision: number; parentRevision: number; source: ChangeSource; kind: ChangeKind; createdAt: number; ranges: Array<{ start: number; end: number; beforeText: string; afterText: string; anchorId?: string; }>; metadata?: { suggestionId?: string; agent?: "chat" | "shorthander" | "reasoner" | "reformatter" | "citations" | "ingester"; batched?: boolean; }; }

Keystroke To Commit Flow

During typing, Rosetta computes one derived editor snapshot per Lexical update. That snapshot carries the plain text, selection offsets, cursor position, slash command context, SmartPhrase context, and context menu selection geometry. JSON serialization waits until the persistence debounce or an explicit flush point such as blur, unload, save, or note switch.

Diffs

Rosetta compares the old note and the new note in three passes. Each pass only runs if the previous one did not produce a reliable result:

  1. Nodes: compare editor subtrees by identity. If a whole paragraph or template block did not change, Rosetta skips it.
  2. Tokens: compare sentence sized chunks or template chunks. Most clinical edits stop here because this is accurate enough for review.
  3. Characters: compare individual characters when token precision drops below 98%. This lets the overlay highlight the exact text that changed.
function computeRanges(prev: string, next: string): RangeDelta[] { const tokenPass = diffByTokens(prev, next); if (tokenPass.precision >= 0.98) return tokenPass.ranges; return diffByCharacters(prev, next).ranges; }

Range Remapping

A suggestion points at specific character offsets. If the user types before the suggestion is applied, those offsets can point at the wrong place. Rosetta shifts each offset by the net length change of every user edit that happened at or before it:

function remapRange(range: { start: number; end: number }, deltas: RangeDelta[]) { let { start, end } = range; for (const delta of deltas) { if (delta.pos <= start) start += delta.netLength; if (delta.pos < end) end += delta.netLength; } return { start, end }; }

Overlap and Conflict Rules

Agent Suggestion Lifecycle

Suggestion Object

type SuggestionStatus = | "pending" | "focused" | "accepted" | "rejected" | "stale" | "superseded"; interface AISuggestion { id: string; revision: number; type: "replace" | "insert" | "delete"; start: number; end: number; replacementText: string; reasoning: string; confidence: number; status: SuggestionStatus; }

Suggestion State Machine

Safe Apply Logic

function applySuggestion(suggestion: AISuggestion) { editor.update(() => { if (isLockedRange(suggestion.start, suggestion.end)) return; const remapped = remapSuggestionAgainstLatestRevision(suggestion); if (!remapped) { markSuggestion(suggestion.id, "stale"); return; } replaceText(remapped.start, remapped.end, remapped.replacementText); appendChangeTransaction({ source: "ai", kind: suggestion.type === "insert" ? "insert" : "replace", metadata: { suggestionId: suggestion.id } }); markSuggestion(suggestion.id, "accepted"); }); }

Visual Indicators

TypeColorBehavior
InsertGreenPreview as additive text
ReplaceYellowOriginal highlighted with replacement preview
DeleteRedOriginal shown with remove indicator

Template Anchors

interface TemplateAnchor { id: string; label: string; isLocked: boolean; nodeKey: string; start: number; end: number; }

Guard Rails

  • Agents cannot modify locked anchors.
  • Bulk accept skips suggestions touching locked anchors.
  • Template updates can move anchors but cannot silently unlock them.
  • Anchor deletion requires explicit user confirmation.

Template Fields

SELECT single choice:

{{SELECT: medication | aspirin, clopidogrel, warfarin}}

MULTISELECT multiple values:

{{MULTISELECT: symptoms | chest pain, dyspnea, diaphoresis}}

DYNAMIC_SELECT options resolved from query:

{{DYNAMIC_SELECT: drug | query: medications for hypertension}}

Field Node Rendering

class FieldNode extends DecoratorNode<JSX.Element> { decorate(): JSX.Element { return ( <FieldComponent type={this.__fieldType} options={this.__options} onSelect={this.handleSelect} /> ); } }

SmartPhrases

Expansion rules for common note sections:

const smartPhrases: SmartPhrase[] = [ { trigger: ".cc", expansion: "Chief Complaint:\n" }, { trigger: ".hpi", expansion: "History of Present Illness:\n" }, { trigger: ".pe", expansion: "Physical Examination:\n" }, { trigger: ".ap", expansion: "Assessment & Plan:\n" } ];

Expansion is treated as a user transaction. Pending agent suggestions touching the trigger span are remapped, and if remap fails they are marked stale.

Plugin Order

<LexicalComposer initialConfig={config}> <RichTextPlugin /> <HistoryPlugin /> <RosettaStateSyncPlugin /> <TemplateLockPlugin /> <TemplateReconciliationPlugin /> <EditorCommandPlugin /> <ClinicalHighlightPlugin /> <IndentGuidesPlugin /> </LexicalComposer>

RosettaStateSyncPlugin owns the external load path and the shared derived editor snapshot. EditorCommandPlugin owns keyboard commands, suggestion commands, and template commands. Clinical highlighting and indent guides run after editor mutations, but both are scheduled outside the immediate typing path.

Keyboard Shortcuts

ShortcutAction
Ctrl/Cmd + SSave the current note (or template, in the template editor)
Ctrl/Cmd + WClose the current note tab
Ctrl/Cmd + ZUndo
Ctrl/Cmd + Shift + ZRedo
Tab / Shift + TabIndent / outdent
Enter or TabAccept a smart phrase expansion
EscDismiss a smart phrase expansion

Data Model

Rosetta stores documents as Lexical node trees. The live node tree is the source of truth while the user is typing. Plain text and persisted JSON are snapshots derived from that tree. Everything else, including pending edits, template anchors, and sync payloads, references positions inside it.

Core Entities

  • Document nodes: block containers such as paragraphs and headings, plus inline marks such as bold, italic, and code.
  • Pending edits: changes proposed by an agent. They live outside the document until the user accepts them.
  • Template anchors: named regions defined by a template. A locked anchor can’t be modified by any agent.
  • Derived editor snapshot: the plain text, selection offsets, cursor position, slash command context, SmartPhrase context, and context menu geometry emitted from a single Lexical update listener.

Persistence Shape

The app persists two synchronized views of the same document:

  • content: plain text used by workflows, search, exports, and legacy integrations.
  • jsonContent: serialized Lexical state used to restore decorators, template fields, suggestions, and editor metadata.

Typing does not regenerate jsonContent on every keystroke. Rosetta updates the live Lexical tree immediately, emits a lightweight derived snapshot for UI consumers, then writes content and jsonContent after the persistence debounce or an explicit flush boundary.

Pending Edit Structure

A pending edit is a proposed change that has not touched the note yet. It stores the exact range it applies to, the original text, the replacement text, the agent’s reason, and the agent that produced it. Because those details travel with the edit, Rosetta can show the preview, record the audit trail, compare it with other suggestions, or mark it stale without changing the document.

interface PendingEdit { id: string; type: "insert" | "replace" | "delete"; start: number; end: number; original: string; replacement: string; reasoning: string; agent: string; }

For the full lifecycle, including how a pending edit is remapped when the user types, how conflicts are handled, and when an edit is marked stale, see the Editor Guide.

Keyboard Shortcuts

Workspace

KeyAction
Ctrl/Cmd + SSave the current note, or the template when the template editor is open
Ctrl/Cmd + WClose the current note tab, or the template editor
EscClose the settings panel

Editing

KeyAction
Ctrl/Cmd + ZUndo
Ctrl/Cmd + Shift + ZRedo
TabIndent the current block, or each block when several are selected
Shift + TabOutdent
/Open slash commands

Smart Phrases

KeyAction
Enter or TabAccept the suggested expansion
EscDismiss the suggestion

Notes

  • Editing shortcuts apply when the editor has focus.
  • Pending agent suggestions are reviewed with the mouse: hover a suggestion to preview the diff, then accept or reject it.

Workflows

Rosetta puts note drafting, templates, and agent tools in one editor.

Document State

Document state is local-first. Keystrokes hit the live editor and the browser’s local store immediately, so typing is never blocked on the network. Signed in, notes and workspace records sync to your account in the background. Signed out, everything stays in local storage on that machine.

Review Workflow

Nothing an agent produces changes a note until you review and accept it.

Typical Note Path

  1. open the note
  2. apply the template
  3. draft or edit
  4. run chat or a workflow
  5. review the suggestion
  6. accept it to revise the note

Workflow results return to chat unless you accept an edit. Rejected suggestions leave the note unchanged.

State Rules

  • Opening a note restores the current note content and template state.
  • While drafting, Lexical owns the live edit. Plain text, selection metadata, and serialized JSON are emitted as debounced snapshots so typing stays responsive.
  • Suggestions remain pending until the user accepts or rejects them.
  • Accepting a suggestion updates the note and creates a revision entry.

Approval Gate

Agent actions that change your workspace pause for confirmation before they apply. The “Auto-approve agent actions” toggle in Settings skips this confirmation step.

Folder Workflow Path

Folder presentation workflows follow a separate branch:

  1. open the folder
  2. collect notes
  3. run the presentation workflow
  4. return the draft to chat

These workflows do not require a single active note, but they do depend on notes existing in the selected folder.

Templates

Templates define note structure and field behavior.

Interactive Fields

  • Text fields: {{chief_complaint}}
  • Select menus: {{SELECT:severity|mild,moderate,severe}}
  • Dates: Auto-formatted date pickers

Locked Anchors

Template regions that agents can’t write to: legal disclaimers, required section headers, billing codes. Only a user edit can change a locked anchor. This keeps compliance-sensitive content stable no matter how many agent runs a note goes through.

Agents

Run agents from chat or by right-clicking selected text. Each run produces a pending suggestion. Nothing writes to the document until you accept it.

AgentWhat it does
ShorthanderExpands medical abbreviations (SOB becomes shortness of breath). Skips locked anchors.
IngesterParses uploaded PDFs and text files into searchable chunks. Keeps the original metadata so you can trace any chunk back to its source.
ReasonerGenerates ranked differentials and problem-based plans. Weighs claims that have retrieved evidence over ones that don’t.
ReformatterRewrites selected text to match a requested format (SOAP, bullet list, table). The meaning stays the same.
CitationsSearches PubMed and attaches references to the relevant claims.

Clinical Tools

During a chat turn the assistant can also call clinical tools directly:

Tool familyCoverage
Terminology lookupsICD-10, LOINC, and RxNorm
Literature and regulatory dataPubMed, OpenFDA, and clinical trial registries
Scores and calculationsCommon scoring systems and clinical math, computed in code so the arithmetic is reliable

For example, asking:

What's the ICD-10 code for community-acquired pneumonia, and calculate the CURB-65 for this patient?

resolves the code through the terminology lookup and computes the score from the values in your note.

Synthetic Cases

You do not need a real chart to use Rosetta. Ask the assistant to generate synthetic cases and it drafts the notes itself, then stores them through the same workspace tools it uses for any other note:

Create a project of 5 synthetic inpatient cases for internal medicine, each with an H&P and a progress note.

The assistant writes the mock notes, calls create_project and create_note to save them, and the result lands in your sidebar as a normal project you can open, edit, and run workflows against. This is useful for trying templates, testing presentations, or building teaching material without touching protected health information.

SmartPhrases

Custom text expansion shortcuts. Type the trigger and accept the expansion with Enter or Tab:

TriggerExpansion
.ccChief Complaint section
.hpiHistory of Present Illness
.rosReview of Systems
.pePhysical Examination
.apAssessment & Plan
.dcDischarge instructions

Slash Commands

Slash commands are custom commands you define and invoke with / in the editor. Each command has a label, a trigger, and options, managed from the Slash Commands panel in the left sidebar. With “AI auto-fill slash defaults” enabled in Settings, highlighting text before creating a command pre-fills those fields from the selection.

Template Pipeline

Templates define both the structure of a note and the rules it has to follow. Rosetta loads templates from a registry, applies them at workspace or note scope, enforces anchors in the editor, and can save edited variants back to the template library.

Template Sources

SourceWhat it contains
built in templatesTemplates shipped with the app
user templatesTemplates saved in versioned local storage
workspace templateDefault template selected for the current workspace session
local note templateA local override of the template anchor set

Apply Order

Template application resolves in this order:

  1. load the template registry
  2. choose the workspace default
  3. open the note
  4. resolve shared or local template mode
  5. render anchors and fields
  6. enforce lock rules

Pipeline Steps

  1. Select template: load the structure and its anchor and field definitions.
  2. Bind field values: resolve SELECT, MULTISELECT, and DYNAMIC_SELECT inputs and insert them at their anchors.
  3. Run agent transforms: agents propose edits against the bound document.
  4. Validate locked regions: any proposed change that overlaps a locked anchor is dropped before it reaches the review queue.
  5. Queue pending edits: remaining suggestions wait for the user to accept or reject them.

Global Vs Local Template Modes

ModeWhat it means
globalThe note follows a shared template definition
localThe note stores its own anchor list and optional custom name

Local mode is useful when a note needs custom headings but you do not want to change the shared template for every future note.

Template Inference

Rosetta can infer template state from the note content itself. If the note headings match a known template anchor sequence, the app can attach that template state. If headings no longer match, the app can clear or replace the template association.

This keeps template behavior aligned with the actual note text.

Template Editor

The template editor supports two formats:

FormatWhat Rosetta stores
naturalTemplate content with readable headings
smartphraseSmartPhrase content with a reusable trigger

When you save template edits, Rosetta can:

  • overwrite the current template
  • save a new template
  • store SmartPhrase metadata alongside the template
  • save the SmartPhrase into the SmartPhrase registry

Structured Fields

Templates can include field metadata such as:

  • text
  • select
  • multiselect
  • dynamic_select

These fields are rendered by the editor and can be extended while editing a template.

RAG

Every agent response in Rosetta is backed by retrieved sources. These include your uploads, your account library, public medical databases, and live web search. Agents do not answer from memory alone.

Architecture

Source documents are split into passages, and each passage is converted into a vector that represents its meaning. A query is turned into a vector the same way. Retrieval returns the passages whose vectors are closest to the query vector (by cosine similarity), combined with keyword scoring so exact terms still count. That’s why a search for “heart failure treatment” can match a passage that says “HFrEF management”: the two mean roughly the same thing, so their vectors sit near each other.

Each source has an “included in ingest” toggle. Only included sources are indexed. Excluding a source removes it from retrieval without deleting it.

Semantic vs Keyword

Keyword Search (Traditional):

Keyword search looks for the exact words you typed. For clinicians, this is like searching a PDF for a medication name, lab name, diagnosis code, or phrase from a guideline. It is precise when the source uses the same wording as your query, but it can miss clinically equivalent language.

Query: "heart failure treatment" Matches: Exact text "heart failure" AND "treatment" Misses: "HFrEF management", "cardiac dysfunction therapy"

Semantic Search (Rosetta):

Semantic search looks for meaning. Rosetta turns your question and each source passage into a numerical representation of clinical meaning, then retrieves passages with similar meaning. This helps when the note uses one phrase and the guideline uses another. For example, a clinician may ask about heart failure treatment, while the source says HFrEF management or GDMT for reduced EF.

Query: "heart failure treatment" Matches: Any semantically similar concepts - "HFrEF management" - "cardiac dysfunction therapy" - "GDMT for reduced EF"

Source Types

  1. Local: sources attached to the current note. Good for uploads tied to a specific patient.
  2. Account: guidelines, protocols, and papers in your persistent library. Indexed once and available to every note on your account.

PubMed Integration

PubMed is a third source type. Unlike local and account sources, it is queried live:

  • The agent rewrites your request into a PubMed search string.
  • Top results come back with their abstracts and metadata.
  • Those abstracts are embedded and mixed into the same retrieval pool as your local and account sources. Citations can come from any of the three.

When a question needs current information that is not in your library or PubMed, the assistant can run a live web search through Tavily, which returns compact results with citations built for use by AI agents.

Web search needs its own key, set under Settings → Tavily web search or supplied through the VITE_TAVILY_API_KEY environment variable. Without a Tavily key the assistant skips web search and falls back to your other sources. As with every other source, results the assistant uses are cited in the response.

Usage Examples

Evidence Based Treatment

Add a guideline PDF to your Account library. Ask “heart failure reduced ejection fraction treatment” in a note. The agent retrieves guidance and generates a plan with citations from the uploaded PDF.

Literature Review

The agent searches PubMed for recent abstracts on a topic and generates a summary with current scoring criteria.

Institutional Protocols

Upload a hospital protocol to the Account library. Query it by name (e.g., “UCSF sepsis bundle timing”) and the agent returns the relevant details.

Retrieval Pipeline

Rosetta retrieval has two stages: ingest and search. Before an agent generates a response, Rosetta pulls supporting passages from your library and the literature, then sends the retrieved passages with the question.

Ingest

1. Add A Source

Sources come from uploads, pasted text, account sync, or PubMed search.

2. Normalize The Payload

Rosetta normalizes the source into a RagSource record.

3. Extract Text When Needed

PDF uploads are parsed in the browser with pdfjs-dist. If Rosetta cannot extract usable text, the source is still stored, but it is marked so it does not enter normal text ingest.

4. Preserve Scope

Each source is treated as either:

  • local
  • account

Account sources can be merged into the current folder when the user is signed in.

Search runs in this order:

  1. collect eligible text
  2. embed sources
  3. embed the query
  4. score matches
  5. return top sources
  6. build the cited prompt

Retrieval Defaults

The semantic search flow:

  • chunks source documents into passages
  • embeds text content with text-embedding-004
  • uses cosine similarity for matching
  • returns a small result set
  • builds the prompt with [Source N] labels

Retrieval Controls

  • Source filters: limit retrieval to a specific library, such as local, account, or public sources.
  • Date windows: limit to a time range, for example guidelines from the last five years.
  • Relevance thresholds: drop chunks below a minimum similarity score so weak matches do not dilute the context.

PubMed Path

PubMed follows a parallel path:

  1. Rosetta searches PubMed
  2. loads summaries and abstracts
  3. converts articles into RAG sources
  4. lets the user add the selected articles to the active context

Citations

When retrieval returns supporting passages, each one is attached to the response as a citation. If nothing was retrieved, the response comes back without citations. Rosetta won’t make one up.

How Citations Appear

Citations show up in two places: inline markers on the claims they support, and a source list under the response.

Start broad-spectrum coverage within 1 hour of recognition [Source 1]. Piperacillin-tazobactam 4.5 g IV q6h is first line; add vancomycin if MRSA risk factors are present [Source 1]. 📚 Sources [1] Sepsis Guidelines 2024

Markers are numbered in the order sources were retrieved. The numbering is per-response: [Source 1] in one answer and [Source 1] in the next can be different documents.

When Citations Are Missing

An answer without expected citations usually means one of three things:

  1. The source is empty, or Rosetta could not extract usable text from it.
  2. The source’s “included in ingest” toggle is off.
  3. The question genuinely didn’t match any passage in your library.

Check the first two in the RAG panel before assuming the third.

API Reference

Rosetta provides a REST API for documents, templates, tool requests, and export workflows.

Base URL

https://philipshih.org/apps/rosetta/api/v1

Authentication

Bearer token in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Rate Limiting

TierLimitBurst
Free100 requests/hour10 concurrent
Professional1,000 requests/hour50 concurrent
Enterprise10,000 requests/hour100 concurrent

Documents API

List Documents

GET /v1/documents

Query Parameters

ParameterTypeDescription
limitintegerResults per page (default: 20, max: 100)
offsetintegerPagination offset (default: 0)
sortstringcreated_at, updated_at, title
orderstringasc or desc
searchstringFull-text search query
tagstringFilter by tag

Example

curl -H "Authorization: Bearer YOUR_API_KEY" \ "https://philipshih.org/apps/rosetta/api/v1/documents?limit=10&sort=created_at&order=desc"

Get Document

GET /v1/documents/:id

Create Document

POST /v1/documents

Request Body

{ "title": "New Progress Note", "content": "Document content in markdown...", "tags": ["progress-note"], "template_id": "tpl_123", "metadata": { "document_type": "progress_note", "date_of_service": "2026-01-20" } }

Update Document

PATCH /v1/documents/:id

Delete Document

DELETE /v1/documents/:id

Delete Response

{ "success": true, "message": "Document moved to trash", "recoverable_until": "2026-02-19T09:15:00Z" }

Templates & Tools

Templates

List Templates

GET /v1/templates

Get Template

GET /v1/templates/:id

Tool Requests

Ask Question

POST /v1/ai/ask

Request Body

{ "question": "What is the treatment for community-acquired pneumonia?", "context": "Outpatient, no comorbidities", "include_citations": true }

Generate Text

POST /v1/ai/generate

Request Body

{ "prompt": "Expand on COPD exacerbation", "context": "Progress note, moderate severity", "max_tokens": 500, "temperature": 0.7 }

Export & Webhooks

Export Document

POST /v1/documents/:id/export

Request Body

{ "format": "pdf", "options": { "include_metadata": true, "include_citations": true, "page_size": "letter", "orientation": "portrait" } }

Supported Formats

  • pdf
  • docx
  • html
  • txt
  • markdown

Webhooks

Subscribe to account events from Settings -> Webhooks.

Available Events

  • document.created
  • document.updated
  • document.deleted
  • export.completed
  • ai.query_completed

Payload Example

{ "event": "document.created", "timestamp": "2026-01-20T10:30:00Z", "data": { "document_id": "doc_abc123", "title": "New Progress Note" } }

Errors & SDKs

Error Response Format

{ "error": { "type": "validation_error", "message": "Invalid request parameters", "details": [ { "field": "title", "issue": "Title is required" } ], "request_id": "req_abc123" } }

HTTP Status Codes

CodeMeaningDescription
200OKSuccessful request
201CreatedResource created successfully
400Bad RequestInvalid request parameters
401UnauthorizedMissing or invalid API key
403ForbiddenInsufficient permissions
404Not FoundResource does not exist
429Too Many RequestsRate limit exceeded
500Internal Server ErrorContact support

Error Types

  • authentication_error
  • validation_error
  • permission_error
  • rate_limit_error
  • not_found_error
  • server_error

SDKs

Python

pip install rosetta-client
from rosetta import RosettaClient client = RosettaClient(api_key="YOUR_API_KEY") documents = client.documents.list(limit=10)

JavaScript / TypeScript

npm install @rosetta/client
import { RosettaClient } from "@rosetta/client"; const client = new RosettaClient({ apiKey: "YOUR_API_KEY" }); const documents = await client.documents.list({ limit: 10 });