Sources list

This commit is contained in:
Mike Thicke 2025-05-09 12:11:14 -04:00
parent 5cabb2aeb3
commit 6f83da2cf6
4 changed files with 52 additions and 16 deletions

24
package-lock.json generated
View file

@ -1728,9 +1728,9 @@
}
},
"node_modules/fdir": {
"version": "6.4.3",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.3.tgz",
"integrity": "sha512-PMXmW2y1hDDfTSRc9gaXIuCCRpuoz3Kaz8cUelp3smouvfT632ozg2vrT6lJsHKKOF59YLbOGfAWGUcKEfRMQw==",
"version": "6.4.4",
"resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz",
"integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==",
"dev": true,
"license": "MIT",
"peerDependencies": {
@ -2667,13 +2667,13 @@
}
},
"node_modules/tinyglobby": {
"version": "0.2.12",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.12.tgz",
"integrity": "sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==",
"version": "0.2.13",
"resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz",
"integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==",
"dev": true,
"license": "MIT",
"dependencies": {
"fdir": "^6.4.3",
"fdir": "^6.4.4",
"picomatch": "^4.0.2"
},
"engines": {
@ -2775,18 +2775,18 @@
"license": "ISC"
},
"node_modules/vite": {
"version": "6.3.1",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.1.tgz",
"integrity": "sha512-kkzzkqtMESYklo96HKKPE5KKLkC1amlsqt+RjFMlX2AvbRB/0wghap19NdBxxwGZ+h/C6DLCrcEphPIItlGrRQ==",
"version": "6.3.5",
"resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz",
"integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"esbuild": "^0.25.0",
"fdir": "^6.4.3",
"fdir": "^6.4.4",
"picomatch": "^4.0.2",
"postcss": "^8.5.3",
"rollup": "^4.34.9",
"tinyglobby": "^0.2.12"
"tinyglobby": "^0.2.13"
},
"bin": {
"vite": "bin/vite.js"

View file

@ -8,11 +8,13 @@ import {
ContextNote,
ChatRequest,
generateChatTitle,
Source,
} from "@/services/model-service";
import { PluginContext, ChangeCallbackContext, AppContext } from "@/CoiChatApp";
import { ChatHistory } from "@/components/ChatHistory";
import { UserInput } from "@/components/UserInput";
import { LinkedNotes } from "@/components/LinkedNotes";
import { SourceList } from "@/components/SourceList";
export interface ChatInterfaceProps {
initialMessages: CoreMessage[];
@ -50,6 +52,8 @@ export const ChatInterface = ({
const [messages, setMessages] = createSignal<CoreMessage[]>(initialMessages);
const [linkedNotes, setLinkedNotes] =
createSignal<TFile[]>(initialLinkedNotes);
const [sources, setSources] = createSignal<Source[]>([]);
const app = useContext(AppContext);
const handleLinkNote = (file: TFile) => {
@ -102,7 +106,7 @@ export const ChatInterface = ({
const responseStream = await generateChatResponse(request, registry);
let accumulatedContent = "";
for await (const chunk of responseStream) {
for await (const chunk of responseStream.textStream) {
accumulatedContent += chunk;
const responseMessage: CoreMessage = {
role: "assistant",
@ -114,6 +118,7 @@ export const ChatInterface = ({
return updatedMessages;
});
}
setSources(await responseStream.sources);
const newTitle = await generateChatTitle(model().id, messages(), registry);
if (onChange) {
onChange(messages(), newTitle, linkedNotes());
@ -123,6 +128,7 @@ export const ChatInterface = ({
return (
<div>
<ChatHistory messages={messages} />
{sources().length > 0 && <SourceList sources={sources()} />}
{linkedNotes().length > 0 && (
<LinkedNotes
notes={linkedNotes()}

View file

@ -0,0 +1,17 @@
import { Source } from "@/services/model-service";
export const SourceList = ({ sources }: { sources: Source[] }) => {
return (
<div>
<ol>
{sources.map((source) => (
<li>
<a href={source.url} target="_blank" rel="noopener noreferrer">
{source.title ?? source.url}
</a>
</li>
))}
</ol>
</div>
);
};

View file

@ -1,4 +1,11 @@
import { streamText, generateText, GenerateTextResult, CoreMessage } from "ai";
import {
streamText,
generateText,
GenerateTextResult,
CoreMessage,
StreamTextResult,
ToolSet,
} from "ai";
import { ModelRegistry, ModelId } from "./model-registry";
export interface ContextNote {
@ -16,6 +23,12 @@ export interface ChatRequest {
contextNotes?: ContextNote[];
}
export interface Source {
id: string;
url: string;
title?: string;
}
/**
* Generates a chat response based on the provided request.
*
@ -27,7 +40,7 @@ export interface ChatRequest {
export async function generateChatResponse(
request: ChatRequest,
registry: ModelRegistry,
): Promise<AsyncIterable<string>> {
): Promise<StreamTextResult<ToolSet, never>> {
const model = registry.getLanguageModel(request.modelId);
// Prepare context from linked notes if any
@ -55,7 +68,7 @@ export async function generateChatResponse(
};
const result = streamText(config);
return result.textStream;
return result;
}
export async function generateChatTitle(