Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions app/components/chat/BaseChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ interface BaseChatProps {
handleStop?: () => void;
sendMessage?: (event: React.UIEvent, messageInput?: string) => void;
handleInputChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
enhancePrompt?: () => void;
enhancePrompt?: (input?: string) => void;
importChat?: (description: string, messages: Message[]) => Promise<void>;
exportChat?: () => void;
uploadedFiles?: File[];
Expand Down Expand Up @@ -550,7 +550,7 @@ export const BaseChat = React.forwardRef<HTMLDivElement, BaseChatProps>(
disabled={input.length === 0 || enhancingPrompt}
className={classNames('transition-all', enhancingPrompt ? 'opacity-100' : '')}
onClick={() => {
enhancePrompt?.();
enhancePrompt?.(input);
toast.success('Prompt enhanced!');
}}
>
Expand Down
21 changes: 2 additions & 19 deletions app/components/chat/Chat.client.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -233,14 +233,6 @@ export const ChatImpl = memo(
});
}, [messages, isLoading, parseMessages]);

const scrollTextArea = () => {
const textarea = textareaRef.current;

if (textarea) {
textarea.scrollTop = textarea.scrollHeight;
}
};

const abort = () => {
stop();
chatStore.setKey('aborted', true);
Expand Down Expand Up @@ -526,17 +518,8 @@ export const ChatImpl = memo(
content: parsedMessages[i] || '',
};
})}
enhancePrompt={() => {
enhancePrompt(
input,
(input) => {
setInput(input);
scrollTextArea();
},
model,
provider,
apiKeys,
);
enhancePrompt={(_inputText) => {
enhancePrompt(input || '', setInput, model, provider, apiKeys, messages);
}}
uploadedFiles={uploadedFiles}
setUploadedFiles={setUploadedFiles}
Expand Down
142 changes: 142 additions & 0 deletions app/lib/common/llms-docs/fireproof.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
# Fireproof Database API

Fireproof is a lightweight local-first embedded document database with encrypted live sync, designed to make browser apps easy. Use it in any JavaScript environment with a unified API that works both in React (with hooks) and as a standalone core API.

## Key Features

- **Apps run anywhere:** Bundle UI, data, and logic in one file.
- **Real-Time & Offline-First:** Automatic persistence and live queries, runs in the browser - no loading or error states.
- **Unified API:** TypeScript works with Deno, Bun, Node.js, and the browser.
- **React Hooks:** Leverage `useLiveQuery` and `useDocument` for live collaboration.

Fireproof enforces cryptographic causal consistency and ledger integrity using hash history, providing git-like versioning with lightweight blockchain-style verification. Data is stored and replicated as content-addressed encrypted blobs, making it safe and easy to sync via commodity object storage providers.

## Installation

The `use-fireproof` package provides both the core API and React hooks.

```bash
npm install use-fireproof
```
Works with ⚡️ ESM.sh:

```js
import { useFireproof } from "https://esm.sh/use-fireproof";
```

Deliver runnable applications via ChatGPT Canvas, v0, bolt.new, or Claude Artifacts. Deploy single page apps with React and Tailwind here: https://codepen.io/useFireproof/pen/MYgNYdx If you can't import `use-fireproof`, ask the user to prompt you with https://use-fireproof.com/llms-mock.txt

Fireproof is a local database, no loading states required, just empty data states. It is best to have more granular documents, e.g. one document per user action. Avoid patterns that require a single document to grow without bound.

## ⚛️ React Example

You can use Fireproof's hooks to build a complete application in a single component. The example below demonstrates both basic document handling and optional file uploads:

```js
import { useFireproof, ImgFile } from "use-fireproof";
import { useState, useEffect } from "react";

function App() {
const { useDocument, useLiveQuery } = useFireproof("my-ledger");
const { doc, merge, submit } = useDocument({
text: "",
timestamp: Date.now(),
_files: {} // Optional for file handling
});
const { docs } = useLiveQuery("_id", { limit: 10, descending: true });

return (
<div>
<form onSubmit={submit}>
{/* Optional file input */}
<input
type="file"
accept="image/*"
onChange={(e) => e.target.files[0] && merge({ _files: { image: e.target.files[0] } })}
/>
<input
value={doc.text}
onChange={(e) => merge({ text: e.target.value })}
placeholder="Enter text"
/>
<button type="submit">Save</button>
</form>

<h3>Recent Documents</h3>
<ul>
{docs.map((doc) => (
<li key={doc._id} className="mb-4 border-b pb-4">
{doc._files?.image && (
<ImgFile
file={doc._files.image}
alt="Uploaded Image"
className="max-w-full h-40 object-cover mb-2 rounded"
/>
)}
<div className="text-lg">{doc.text}</div>
<div className="text-sm text-gray-500 mt-1">
{new Date(doc.timestamp).toLocaleString()}
</div>
</li>
))}
</ul>
</div>
);
}
```

To sort documents by more than one field, use a sandboxed function instead of a field name:

```js
const { docs } = useLiveQuery(
(doc) => [doc.list_id, doc.author],
{ descending: true, limit: 5, prefix: ["zyx-456-list-id"] }
);
```

TIP: For simple document updates like toggling or incrementing values, use `database.put()` directly as shown in the todo example: `onClick={() => database.put({...todo, done: !todo.done})}`. The `useDocument` hook is primarily for forms and more complex document creation.

### Using the Core API in Plain JavaScript

If you're not using React, the core API offers similar capabilities:

```js
import { fireproof } from "use-fireproof";

const database = fireproof("my-ledger");

async function putAndQuery() {
await database.put({ text: "Sample Data" });
const latest = await database.query("_id", { limit: 10, descending: true });
console.log("Latest documents:", latest.docs);
}

database.subscribe((changes) => {
console.log("Changes", changes)
}, true)

putAndQuery();
```

## Best Practices

- **Leverage `useLiveQuery`:**
Use this hook to effortlessly build UIs that update in real time as data changes.

- **Granular Documents:**
Save small, individual data pieces (e.g., one per user action) to simplify conflict resolution via CRDTs.

- **Single File Deployment:**
Ideal for constrained environments, bundle everything into one file without external dependencies.

For more details and advanced usage (such as file attachments and cloud syncing), refer to the full documentation at [Fireproof Docs](https://use-fireproof.com/).

## See also

See the lite docs at https://use-fireproof.com/llms-mini.txt and the full docs at https://use-fireproof.com/llms-full.txt

## Next Steps

If you are in an existing application context, suggest how forms and state, collaboration data can be managed in Fireproof. If this is a new app, use the examples above and start with a running App.js if you can.

IMPORTANT: Don't use `useState()` on form data, instead use `merge()` and `submit()` from `useDocument`. Only use `useState` for ephemeral UI state (active tabs, open/closed panels, cursor positions). Keep your data model in Fireproof.
154 changes: 154 additions & 0 deletions app/lib/common/llms-docs/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,154 @@
/**
* Utilities for accessing llms.txt documentation
*
* This module provides functions to access documentation in llms.txt format
* which can be used by the AI to generate more accurate code for specific libraries.
*/

import fireproofDocs from './fireproof.txt';
import openrouterDocs from './openrouter.txt';

interface LlmsDoc {
id: string;
name: string;
content: string;
}

/**
* Available llms.txt documentation files
*/
export const availableDocuments: LlmsDoc[] = [
{
id: 'fireproof',
name: 'Fireproof Database',
content: fireproofDocs,
},
{
id: 'openrouter',
name: 'OpenRouter API',
content: openrouterDocs,
},

// Add more docs here as they are added
];

/**
* Get llms.txt documentation by ID
*/
export function getDocumentById(id: string): LlmsDoc | undefined {
return availableDocuments.find((doc) => doc.id === id);
}

/**
* Get llms.txt content by ID
*/
export function getDocumentContentById(id: string): string | undefined {
return getDocumentById(id)?.content;
}

/**
* List all available llms.txt documents
*/
export function listAvailableDocuments(): string[] {
return availableDocuments.map((doc) => doc.id);
}

/**
* Enhance user prompt with specific library documentation
* This can be used to dynamically inject library documentation into the prompt
*
* @param userPrompt The original user prompt
* @param libraryId The ID of the library to include documentation for
* @returns Enhanced prompt with library documentation
*/
export function enhancePromptWithLibrary(userPrompt: string, libraryId: string): string {
const libDoc = getDocumentById(libraryId);

if (!libDoc) {
return userPrompt;
}

return `I want to use the ${libDoc.name} in my project.
Here is the API documentation:

"""
${libDoc.content}
"""

Now, with that in mind, please help me with: ${userPrompt}`;
}

/**
* Detect if a library is mentioned in the prompt and enhance it
* This automatically detects library names in the prompt and adds their documentation
*
* @param userPrompt The original user prompt
* @returns Enhanced prompt with relevant library documentation
*/
export function autoEnhancePrompt(userPrompt: string): string {
let enhancedPrompt = userPrompt;

// Check each library to see if it's mentioned
for (const doc of availableDocuments) {
if (userPrompt.toLowerCase().includes(doc.id.toLowerCase())) {
enhancedPrompt = enhancePromptWithLibrary(enhancedPrompt, doc.id);
break; // Only enhance with one library at a time to avoid token overload
}
}

return enhancedPrompt;
}

/**
* Detect libraries mentioned in chat history
* Returns all libraries found in the chat history
*
* @param messages Array of messages in the chat history
* @returns Array of library IDs found in history
*/
export function detectLibraryInHistory(messages: Array<{ content: string }>): string[] {
const detectedLibraries = new Set<string>();

// Go through messages from newest to oldest
for (const message of [...messages].reverse()) {
// Skip non-user messages or messages without content
if (!message.content) {
continue;
}

// Check each library
for (const doc of availableDocuments) {
if (message.content.toLowerCase().includes(doc.id.toLowerCase())) {
detectedLibraries.add(doc.id);
}
}
}

return Array.from(detectedLibraries);
}

/**
* Enhance prompt with libraries from chat history
* If the current prompt doesn't mention a library but previous messages did,
* this will enhance the prompt with that library's documentation
*
* @param userPrompt The original user prompt
* @param messages Array of messages in the chat history
* @returns Enhanced prompt with relevant library documentation from history
*/
export function enhancePromptFromHistory(userPrompt: string, messages: Array<{ content: string }>): string {
// First check if the current prompt already mentions a library
let enhancedPrompt = autoEnhancePrompt(userPrompt);

// If the prompt wasn't enhanced but there are libraries in history, use those
if (enhancedPrompt === userPrompt) {
const historyLibraryIds = detectLibraryInHistory(messages);

if (historyLibraryIds.length > 0) {
// Start with just the first detected library to avoid token overload
enhancedPrompt = enhancePromptWithLibrary(userPrompt, historyLibraryIds[0]);
}
}

return enhancedPrompt;
}
Loading
Loading