mirror of
https://github.com/run-llama/LlamaIndexTS.git
synced 2026-07-16 07:14:29 -04:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9cf88e9f3f | |||
| 75d6e29187 | |||
| 132517877e | |||
| 299008b34f | |||
| 482ed67690 |
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/doc
|
# @llamaindex/doc
|
||||||
|
|
||||||
|
## 0.2.1
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.2.0
|
## 0.2.0
|
||||||
|
|
||||||
### Minor Changes
|
### Minor Changes
|
||||||
|
|||||||
@@ -4,6 +4,8 @@ const withMDX = createMDX();
|
|||||||
|
|
||||||
/** @type {import('next').NextConfig} */
|
/** @type {import('next').NextConfig} */
|
||||||
const config = {
|
const config = {
|
||||||
|
// default timeout for static generation is 60s, but we need to increase it to 10 minutes due to the large number of document pages
|
||||||
|
staticPageGenerationTimeout: 600,
|
||||||
reactStrictMode: true,
|
reactStrictMode: true,
|
||||||
eslint: {
|
eslint: {
|
||||||
ignoreDuringBuilds: true,
|
ignoreDuringBuilds: true,
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/doc",
|
"name": "@llamaindex/doc",
|
||||||
"version": "0.2.0",
|
"version": "0.2.1",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"postinstall": "fumadocs-mdx",
|
"postinstall": "fumadocs-mdx",
|
||||||
|
|||||||
@@ -7,9 +7,59 @@ A tool can be called to perform custom actions, or retrieve extra information ba
|
|||||||
A result from a tool call can be used by subsequent steps in a workflow, or to compute a final answer.
|
A result from a tool call can be used by subsequent steps in a workflow, or to compute a final answer.
|
||||||
For example, a "weather tool" could fetch some live weather information from a geographical location.
|
For example, a "weather tool" could fetch some live weather information from a geographical location.
|
||||||
|
|
||||||
|
## Tool Function
|
||||||
|
|
||||||
|
The `tool` function is a utility provided to define a tool that can be used by an agent. It takes a function and a configuration object as arguments. The configuration object includes the tool's name, description, and parameters.
|
||||||
|
|
||||||
|
### Parameters with Zod
|
||||||
|
|
||||||
|
The `parameters` field in the tool configuration is defined using `zod`, a TypeScript-first schema declaration and validation library. `zod` allows you to specify the expected structure and types of the input parameters, ensuring that the data passed to the tool is valid.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
```ts
|
||||||
|
import { agent, tool } from "llamaindex";
|
||||||
|
import { z } from "zod";
|
||||||
|
|
||||||
|
// first arg is LLM input, second is bound arg
|
||||||
|
const queryKnowledgeBase = async ({ question }, { userToken }) => {
|
||||||
|
const response = await fetch(`https://knowledge-base.com?token=${userToken}&query=${question}`);
|
||||||
|
// ...
|
||||||
|
};
|
||||||
|
|
||||||
|
// define tool with zod validation
|
||||||
|
const kbTool = tool(queryKnowledgeBase, {
|
||||||
|
name: 'queryKnowledgeBase',
|
||||||
|
description: 'Query knowledge base',
|
||||||
|
parameters: z.object({
|
||||||
|
question: z.string({
|
||||||
|
description: 'The user question',
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
```
|
||||||
|
In this example, `z.object` is used to define a schema for the `parameters` where `question` is expected to be a string. This ensures that any input to the tool adheres to the specified structure, providing a layer of type safety and validation.
|
||||||
|
|
||||||
|
|
||||||
|
## Built-in tools
|
||||||
|
|
||||||
|
You can import built-in tools from the `@llamaindex/tools` package.
|
||||||
|
|
||||||
|
```ts
|
||||||
|
import { agent } from "llamaindex";
|
||||||
|
import { wiki } from "@llamaindex/tools";
|
||||||
|
|
||||||
|
const researchAgent = agent({
|
||||||
|
name: "WikiAgent",
|
||||||
|
description: "Gathering information from the internet",
|
||||||
|
systemPrompt: `You are a research agent. Your role is to gather information from the internet using the provided tools.`,
|
||||||
|
tools: [wiki()],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
## Function tool
|
## Function tool
|
||||||
|
|
||||||
Function tools are implemented with the `FunctionTool` class.
|
You can still use the `FunctionTool` class to define a tool.
|
||||||
A `FunctionTool` is constructed from a function with signature
|
A `FunctionTool` is constructed from a function with signature
|
||||||
```ts
|
```ts
|
||||||
(input: T, additionalArg?: AdditionalToolArgument) => R
|
(input: T, additionalArg?: AdditionalToolArgument) => R
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/cloudflare-worker-agent-test
|
# @llamaindex/cloudflare-worker-agent-test
|
||||||
|
|
||||||
|
## 0.0.147
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.0.146
|
## 0.0.146
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/cloudflare-worker-agent-test",
|
"name": "@llamaindex/cloudflare-worker-agent-test",
|
||||||
"version": "0.0.146",
|
"version": "0.0.147",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/next-agent-test
|
# @llamaindex/next-agent-test
|
||||||
|
|
||||||
|
## 0.1.147
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.1.146
|
## 0.1.146
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/next-agent-test",
|
"name": "@llamaindex/next-agent-test",
|
||||||
"version": "0.1.146",
|
"version": "0.1.147",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# test-edge-runtime
|
# test-edge-runtime
|
||||||
|
|
||||||
|
## 0.1.146
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.1.145
|
## 0.1.145
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/nextjs-edge-runtime-test",
|
"name": "@llamaindex/nextjs-edge-runtime-test",
|
||||||
"version": "0.1.145",
|
"version": "0.1.146",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/next-node-runtime
|
# @llamaindex/next-node-runtime
|
||||||
|
|
||||||
|
## 0.1.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.1.12
|
## 0.1.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/next-node-runtime-test",
|
"name": "@llamaindex/next-node-runtime-test",
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# vite-import-llamaindex
|
# vite-import-llamaindex
|
||||||
|
|
||||||
|
## 0.0.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.0.12
|
## 0.0.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "vite-import-llamaindex",
|
"name": "vite-import-llamaindex",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.0.12",
|
"version": "0.0.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/waku-query-engine-test
|
# @llamaindex/waku-query-engine-test
|
||||||
|
|
||||||
|
## 0.0.147
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.0.146
|
## 0.0.146
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/waku-query-engine-test",
|
"name": "@llamaindex/waku-query-engine-test",
|
||||||
"version": "0.0.146",
|
"version": "0.0.147",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
import {
|
||||||
|
AgentStream,
|
||||||
|
AgentToolCallResult,
|
||||||
|
Document,
|
||||||
|
VectorStoreIndex,
|
||||||
|
agent,
|
||||||
|
openai,
|
||||||
|
} from "llamaindex";
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
const index = await VectorStoreIndex.fromDocuments([
|
||||||
|
new Document({
|
||||||
|
text: "Cats have a specialized collarbone that allows them to always land on their feet when they fall.",
|
||||||
|
}),
|
||||||
|
new Document({
|
||||||
|
text: "Dogs have a sense of smell that is 10,000 to 100,000 times more acute than humans.",
|
||||||
|
}),
|
||||||
|
new Document({
|
||||||
|
text: "Cats are known for their agility and ability to jump high.",
|
||||||
|
}),
|
||||||
|
]);
|
||||||
|
|
||||||
|
const myAgent = agent({
|
||||||
|
llm: openai({ model: "gpt-4o" }),
|
||||||
|
tools: [
|
||||||
|
index.queryTool({
|
||||||
|
options: { similarityTopK: 2 },
|
||||||
|
includeSourceNodes: true,
|
||||||
|
}),
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
const context = myAgent.run("The fact about cats");
|
||||||
|
|
||||||
|
for await (const event of context) {
|
||||||
|
if (event instanceof AgentToolCallResult) {
|
||||||
|
console.log(
|
||||||
|
"Using these retrieved information to answer the question:\n",
|
||||||
|
event.data.toolOutput.result,
|
||||||
|
);
|
||||||
|
} else if (event instanceof AgentStream) {
|
||||||
|
for (const chunk of event.data.delta) {
|
||||||
|
process.stdout.write(chunk);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void main().then(() => {
|
||||||
|
console.log("Done");
|
||||||
|
});
|
||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/autotool
|
# @llamaindex/autotool
|
||||||
|
|
||||||
|
## 6.0.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 6.0.12
|
## 6.0.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,5 +1,13 @@
|
|||||||
# @llamaindex/autotool-01-node-example
|
# @llamaindex/autotool-01-node-example
|
||||||
|
|
||||||
|
## 0.0.94
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
- @llamaindex/autotool@6.0.13
|
||||||
|
|
||||||
## 0.0.93
|
## 0.0.93
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -13,5 +13,5 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
|
"start": "node --import tsx --import @llamaindex/autotool/node ./src/index.ts"
|
||||||
},
|
},
|
||||||
"version": "0.0.93"
|
"version": "0.0.94"
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
|
"url": "git+https://github.com/run-llama/LlamaIndexTS.git",
|
||||||
"directory": "packages/autotool"
|
"directory": "packages/autotool"
|
||||||
},
|
},
|
||||||
"version": "6.0.12",
|
"version": "6.0.13",
|
||||||
"description": "auto transpile your JS function to LLM Agent compatible",
|
"description": "auto transpile your JS function to LLM Agent compatible",
|
||||||
"files": [
|
"files": [
|
||||||
"dist",
|
"dist",
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# @llamaindex/community
|
# @llamaindex/community
|
||||||
|
|
||||||
|
## 0.0.92
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 1325178: fix: stringify all tool results for anthropic on bedrock
|
||||||
|
|
||||||
## 0.0.91
|
## 0.0.91
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/community",
|
"name": "@llamaindex/community",
|
||||||
"description": "Community package for LlamaIndexTS",
|
"description": "Community package for LlamaIndexTS",
|
||||||
"version": "0.0.91",
|
"version": "0.0.92",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "dist/type/index.d.ts",
|
"types": "dist/type/index.d.ts",
|
||||||
"main": "dist/cjs/index.js",
|
"main": "dist/cjs/index.js",
|
||||||
|
|||||||
@@ -111,7 +111,7 @@ export const mapChatMessagesToAnthropicMessages = <
|
|||||||
{
|
{
|
||||||
type: "tool_result",
|
type: "tool_result",
|
||||||
tool_use_id: msg.options.toolResult.id,
|
tool_use_id: msg.options.toolResult.id,
|
||||||
content: msg.options.toolResult.result,
|
content: JSON.stringify(msg.options.toolResult.result),
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/experimental
|
# @llamaindex/experimental
|
||||||
|
|
||||||
|
## 0.0.163
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.0.162
|
## 0.0.162
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/experimental",
|
"name": "@llamaindex/experimental",
|
||||||
"description": "Experimental package for LlamaIndexTS",
|
"description": "Experimental package for LlamaIndexTS",
|
||||||
"version": "0.0.162",
|
"version": "0.0.163",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"types": "dist/type/index.d.ts",
|
"types": "dist/type/index.d.ts",
|
||||||
"main": "dist/cjs/index.js",
|
"main": "dist/cjs/index.js",
|
||||||
|
|||||||
@@ -1,5 +1,11 @@
|
|||||||
# llamaindex
|
# llamaindex
|
||||||
|
|
||||||
|
## 0.9.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 75d6e29: feat: response source nodes in query tool output
|
||||||
|
|
||||||
## 0.9.12
|
## 0.9.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "llamaindex",
|
"name": "llamaindex",
|
||||||
"version": "0.9.12",
|
"version": "0.9.13",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
|
|||||||
@@ -41,6 +41,7 @@ export type QueryToolParams = (
|
|||||||
) & {
|
) & {
|
||||||
responseSynthesizer?: BaseSynthesizer;
|
responseSynthesizer?: BaseSynthesizer;
|
||||||
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
|
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
|
||||||
|
includeSourceNodes?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -98,6 +99,7 @@ export abstract class BaseIndex<T> {
|
|||||||
return new QueryEngineTool({
|
return new QueryEngineTool({
|
||||||
queryEngine: this.asQueryEngine(params),
|
queryEngine: this.asQueryEngine(params),
|
||||||
metadata: params?.metadata,
|
metadata: params?.metadata,
|
||||||
|
includeSourceNodes: params?.includeSourceNodes ?? false,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import type { JSONValue } from "@llamaindex/core/global";
|
||||||
import type { BaseTool, ToolMetadata } from "@llamaindex/core/llms";
|
import type { BaseTool, ToolMetadata } from "@llamaindex/core/llms";
|
||||||
import type { BaseQueryEngine } from "@llamaindex/core/query-engine";
|
import type { BaseQueryEngine } from "@llamaindex/core/query-engine";
|
||||||
import type { JSONSchemaType } from "ajv";
|
import type { JSONSchemaType } from "ajv";
|
||||||
@@ -20,6 +21,7 @@ const DEFAULT_PARAMETERS: JSONSchemaType<QueryEngineParam> = {
|
|||||||
export type QueryEngineToolParams = {
|
export type QueryEngineToolParams = {
|
||||||
queryEngine: BaseQueryEngine;
|
queryEngine: BaseQueryEngine;
|
||||||
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
|
metadata?: ToolMetadata<JSONSchemaType<QueryEngineParam>> | undefined;
|
||||||
|
includeSourceNodes?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type QueryEngineParam = {
|
export type QueryEngineParam = {
|
||||||
@@ -29,19 +31,32 @@ export type QueryEngineParam = {
|
|||||||
export class QueryEngineTool implements BaseTool<QueryEngineParam> {
|
export class QueryEngineTool implements BaseTool<QueryEngineParam> {
|
||||||
private queryEngine: BaseQueryEngine;
|
private queryEngine: BaseQueryEngine;
|
||||||
metadata: ToolMetadata<JSONSchemaType<QueryEngineParam>>;
|
metadata: ToolMetadata<JSONSchemaType<QueryEngineParam>>;
|
||||||
|
includeSourceNodes: boolean;
|
||||||
|
|
||||||
constructor({ queryEngine, metadata }: QueryEngineToolParams) {
|
constructor({
|
||||||
|
queryEngine,
|
||||||
|
metadata,
|
||||||
|
includeSourceNodes,
|
||||||
|
}: QueryEngineToolParams) {
|
||||||
this.queryEngine = queryEngine;
|
this.queryEngine = queryEngine;
|
||||||
this.metadata = {
|
this.metadata = {
|
||||||
name: metadata?.name ?? DEFAULT_NAME,
|
name: metadata?.name ?? DEFAULT_NAME,
|
||||||
description: metadata?.description ?? DEFAULT_DESCRIPTION,
|
description: metadata?.description ?? DEFAULT_DESCRIPTION,
|
||||||
parameters: metadata?.parameters ?? DEFAULT_PARAMETERS,
|
parameters: metadata?.parameters ?? DEFAULT_PARAMETERS,
|
||||||
};
|
};
|
||||||
|
this.includeSourceNodes = includeSourceNodes ?? false;
|
||||||
}
|
}
|
||||||
|
|
||||||
async call({ query }: QueryEngineParam) {
|
async call({ query }: QueryEngineParam) {
|
||||||
const response = await this.queryEngine.query({ query });
|
const response = await this.queryEngine.query({ query });
|
||||||
|
|
||||||
return response.message.content;
|
if (!this.includeSourceNodes) {
|
||||||
|
return { content: response.message.content };
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
content: response.message.content,
|
||||||
|
sourceNodes: response.sourceNodes,
|
||||||
|
} as unknown as JSONValue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,14 @@
|
|||||||
# @llamaindex/server
|
# @llamaindex/server
|
||||||
|
|
||||||
|
## 0.0.3
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- 299008b: feat: copy create-llama to @llamaindex/servers
|
||||||
|
- 75d6e29: feat: response source nodes in query tool output
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.0.2
|
## 0.0.2
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
@@ -3,12 +3,7 @@
|
|||||||
# dependencies
|
# dependencies
|
||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.*
|
.pnp.js
|
||||||
.yarn/*
|
|
||||||
!.yarn/patches
|
|
||||||
!.yarn/plugins
|
|
||||||
!.yarn/releases
|
|
||||||
!.yarn/versions
|
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
@@ -28,10 +23,9 @@
|
|||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
.pnpm-debug.log*
|
|
||||||
|
|
||||||
# env files (can opt-in for committing if needed)
|
# local env files
|
||||||
.env*
|
.env*.local
|
||||||
|
|
||||||
# vercel
|
# vercel
|
||||||
.vercel
|
.vercel
|
||||||
@@ -39,3 +33,7 @@ yarn-error.log*
|
|||||||
# typescript
|
# typescript
|
||||||
*.tsbuildinfo
|
*.tsbuildinfo
|
||||||
next-env.d.ts
|
next-env.d.ts
|
||||||
|
|
||||||
|
output/
|
||||||
|
|
||||||
|
!lib/
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"trailingComma": "all"
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,31 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ChatSection as ChatSectionUI } from "@llamaindex/chat-ui";
|
||||||
|
import "@llamaindex/chat-ui/styles/markdown.css";
|
||||||
|
import "@llamaindex/chat-ui/styles/pdf.css";
|
||||||
|
import { useChat } from "ai/react";
|
||||||
|
import CustomChatInput from "./ui/chat/chat-input";
|
||||||
|
import CustomChatMessages from "./ui/chat/chat-messages";
|
||||||
|
import { getConfig } from "./ui/lib/utils";
|
||||||
|
|
||||||
|
export default function ChatSection() {
|
||||||
|
const handler = useChat({
|
||||||
|
api: getConfig("CHAT_API"),
|
||||||
|
onError: (error: unknown) => {
|
||||||
|
if (!(error instanceof Error)) throw error;
|
||||||
|
let errorMessage: string;
|
||||||
|
try {
|
||||||
|
errorMessage = JSON.parse(error.message).detail;
|
||||||
|
} catch (e) {
|
||||||
|
errorMessage = error.message;
|
||||||
|
}
|
||||||
|
alert(errorMessage);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
return (
|
||||||
|
<ChatSectionUI handler={handler} className="h-full w-full">
|
||||||
|
<CustomChatMessages />
|
||||||
|
<CustomChatInput />
|
||||||
|
</ChatSectionUI>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
export default function Header() {
|
||||||
|
return (
|
||||||
|
<div className="z-10 w-full max-w-5xl items-center justify-between font-mono text-sm lg:flex">
|
||||||
|
<p className="bg-linear-to-b fixed left-0 top-0 flex w-full justify-center border-b border-gray-300 from-zinc-200 pb-6 pt-8 backdrop-blur-2xl lg:static lg:w-auto lg:rounded-xl lg:border lg:bg-gray-200 lg:p-4 dark:border-neutral-800 dark:bg-zinc-800/30 dark:from-inherit lg:dark:bg-zinc-800/30">
|
||||||
|
Get started by editing
|
||||||
|
<code className="font-mono font-bold">app/page.tsx</code>
|
||||||
|
</p>
|
||||||
|
<div className="bg-linear-to-t fixed bottom-0 left-0 mb-4 flex h-auto w-full items-end justify-center from-white via-white lg:static lg:mb-0 lg:w-auto lg:bg-none dark:from-black dark:via-black">
|
||||||
|
<a
|
||||||
|
href="https://www.llamaindex.ai/"
|
||||||
|
className="font-nunito flex items-center justify-center gap-2 text-lg font-bold"
|
||||||
|
>
|
||||||
|
<span>Built by LlamaIndex</span>
|
||||||
|
<img
|
||||||
|
className="h-[40px] w-[40px] rounded-xl"
|
||||||
|
src="/llama.png"
|
||||||
|
alt="Llama Logo"
|
||||||
|
/>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||||
|
import { ChevronDown } from "lucide-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const Accordion = AccordionPrimitive.Root;
|
||||||
|
|
||||||
|
const AccordionItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn("border-b", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
AccordionItem.displayName = "AccordionItem";
|
||||||
|
|
||||||
|
const AccordionTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Header className="flex">
|
||||||
|
<AccordionPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex flex-1 items-center justify-between py-4 text-left text-sm font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<ChevronDown className="h-4 w-4 shrink-0 text-neutral-500 transition-transform duration-200 dark:text-neutral-400" />
|
||||||
|
</AccordionPrimitive.Trigger>
|
||||||
|
</AccordionPrimitive.Header>
|
||||||
|
));
|
||||||
|
AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
|
const AccordionContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof AccordionPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<AccordionPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className="data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down overflow-hidden text-sm"
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||||
|
</AccordionPrimitive.Content>
|
||||||
|
));
|
||||||
|
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
export { Accordion, AccordionContent, AccordionItem, AccordionTrigger };
|
||||||
@@ -0,0 +1,56 @@
|
|||||||
|
import { Slot } from "@radix-ui/react-slot";
|
||||||
|
import { cva, type VariantProps } from "class-variance-authority";
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const buttonVariants = cva(
|
||||||
|
"inline-flex items-center justify-center whitespace-nowrap rounded-md text-sm font-medium ring-offset-background transition-colors focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50",
|
||||||
|
{
|
||||||
|
variants: {
|
||||||
|
variant: {
|
||||||
|
default: "bg-primary text-primary-foreground hover:bg-primary/90",
|
||||||
|
destructive:
|
||||||
|
"bg-destructive text-destructive-foreground hover:bg-destructive/90",
|
||||||
|
outline:
|
||||||
|
"border border-input bg-background hover:bg-accent hover:text-accent-foreground",
|
||||||
|
secondary:
|
||||||
|
"bg-secondary text-secondary-foreground hover:bg-secondary/80",
|
||||||
|
ghost: "hover:bg-accent hover:text-accent-foreground",
|
||||||
|
link: "text-primary underline-offset-4 hover:underline",
|
||||||
|
},
|
||||||
|
size: {
|
||||||
|
default: "h-10 px-4 py-2",
|
||||||
|
sm: "h-9 rounded-md px-3",
|
||||||
|
lg: "h-11 rounded-md px-8",
|
||||||
|
icon: "h-10 w-10",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
defaultVariants: {
|
||||||
|
variant: "default",
|
||||||
|
size: "default",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export interface ButtonProps
|
||||||
|
extends React.ButtonHTMLAttributes<HTMLButtonElement>,
|
||||||
|
VariantProps<typeof buttonVariants> {
|
||||||
|
asChild?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = React.forwardRef<HTMLButtonElement, ButtonProps>(
|
||||||
|
({ className, variant, size, asChild = false, ...props }, ref) => {
|
||||||
|
const Comp = asChild ? Slot : "button";
|
||||||
|
return (
|
||||||
|
<Comp
|
||||||
|
className={cn(buttonVariants({ variant, size, className }))}
|
||||||
|
ref={ref}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
);
|
||||||
|
Button.displayName = "Button";
|
||||||
|
|
||||||
|
export { Button, buttonVariants };
|
||||||
@@ -0,0 +1,82 @@
|
|||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const Card = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"rounded-xl border border-neutral-200 bg-white text-neutral-950 shadow-sm dark:border-neutral-800 dark:bg-neutral-950 dark:text-neutral-50",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
Card.displayName = "Card";
|
||||||
|
|
||||||
|
const CardHeader = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex flex-col space-y-1.5 p-6", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardHeader.displayName = "CardHeader";
|
||||||
|
|
||||||
|
const CardTitle = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("font-semibold leading-none tracking-tight", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardTitle.displayName = "CardTitle";
|
||||||
|
|
||||||
|
const CardDescription = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("text-sm text-neutral-500 dark:text-neutral-400", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardDescription.displayName = "CardDescription";
|
||||||
|
|
||||||
|
const CardContent = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
|
||||||
|
));
|
||||||
|
CardContent.displayName = "CardContent";
|
||||||
|
|
||||||
|
const CardFooter = React.forwardRef<
|
||||||
|
HTMLDivElement,
|
||||||
|
React.HTMLAttributes<HTMLDivElement>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
className={cn("flex items-center p-6 pt-0", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
CardFooter.displayName = "CardFooter";
|
||||||
|
|
||||||
|
export {
|
||||||
|
Card,
|
||||||
|
CardContent,
|
||||||
|
CardDescription,
|
||||||
|
CardFooter,
|
||||||
|
CardHeader,
|
||||||
|
CardTitle,
|
||||||
|
};
|
||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { useChatMessage } from "@llamaindex/chat-ui";
|
||||||
|
import { User2 } from "lucide-react";
|
||||||
|
|
||||||
|
export function ChatMessageAvatar() {
|
||||||
|
const { message } = useChatMessage();
|
||||||
|
if (message.role === "user") {
|
||||||
|
return (
|
||||||
|
<div className="bg-background flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border shadow-sm">
|
||||||
|
<User2 className="h-4 w-4" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex h-8 w-8 shrink-0 select-none items-center justify-center rounded-md border bg-black text-white shadow-sm">
|
||||||
|
<img
|
||||||
|
className="h-[40px] w-[40px] rounded-xl object-contain"
|
||||||
|
src="/llama.png"
|
||||||
|
alt="Llama Logo"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ChatInput, useChatUI, useFile } from "@llamaindex/chat-ui";
|
||||||
|
import { DocumentInfo, ImagePreview } from "@llamaindex/chat-ui/widgets";
|
||||||
|
import { getConfig } from "../lib/utils";
|
||||||
|
import { LlamaCloudSelector } from "./custom/llama-cloud-selector";
|
||||||
|
|
||||||
|
export default function CustomChatInput() {
|
||||||
|
const { requestData, isLoading, input } = useChatUI();
|
||||||
|
const uploadAPI = getConfig("UPLOAD_API") ?? "";
|
||||||
|
const llamaCloudAPI = getConfig("LLAMA_CLOUD_API") ?? "";
|
||||||
|
const {
|
||||||
|
imageUrl,
|
||||||
|
setImageUrl,
|
||||||
|
uploadFile,
|
||||||
|
files,
|
||||||
|
removeDoc,
|
||||||
|
reset,
|
||||||
|
getAnnotations,
|
||||||
|
} = useFile({ uploadAPI });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles file uploads. Overwrite to hook into the file upload behavior.
|
||||||
|
* @param file The file to upload
|
||||||
|
*/
|
||||||
|
const handleUploadFile = async (file: File) => {
|
||||||
|
// There's already an image uploaded, only allow one image at a time
|
||||||
|
if (imageUrl) {
|
||||||
|
alert("You can only upload one image at a time.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
// Upload the file and send with it the current request data
|
||||||
|
await uploadFile(file, requestData);
|
||||||
|
} catch (error: unknown) {
|
||||||
|
// Show error message if upload fails
|
||||||
|
alert(
|
||||||
|
error instanceof Error ? error.message : "An unknown error occurred",
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Get references to the upload files in message annotations format, see https://github.com/run-llama/chat-ui/blob/main/packages/chat-ui/src/hook/use-file.tsx#L56
|
||||||
|
const annotations = getAnnotations();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ChatInput
|
||||||
|
className="rounded-xl shadow-xl"
|
||||||
|
resetUploadedFiles={reset}
|
||||||
|
annotations={annotations}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
{/* Image preview section */}
|
||||||
|
{imageUrl && (
|
||||||
|
<ImagePreview url={imageUrl} onRemove={() => setImageUrl(null)} />
|
||||||
|
)}
|
||||||
|
{/* Document previews section */}
|
||||||
|
{files.length > 0 && (
|
||||||
|
<div className="flex w-full gap-4 overflow-auto py-2">
|
||||||
|
{files.map((file) => (
|
||||||
|
<DocumentInfo
|
||||||
|
key={file.id}
|
||||||
|
document={{ url: file.url, sources: [] }}
|
||||||
|
className="mb-2 mt-2"
|
||||||
|
onRemove={() => removeDoc(file)}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<ChatInput.Form>
|
||||||
|
<ChatInput.Field />
|
||||||
|
{uploadAPI && <ChatInput.Upload onUpload={handleUploadFile} />}
|
||||||
|
{llamaCloudAPI && <LlamaCloudSelector />}
|
||||||
|
<ChatInput.Submit
|
||||||
|
disabled={
|
||||||
|
isLoading || (!input.trim() && files.length === 0 && !imageUrl)
|
||||||
|
}
|
||||||
|
/>
|
||||||
|
</ChatInput.Form>
|
||||||
|
</ChatInput>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
import { ChatMessage } from "@llamaindex/chat-ui";
|
||||||
|
import { DeepResearchCard } from "./custom/deep-research-card";
|
||||||
|
import { ToolAnnotations } from "./tools/chat-tools";
|
||||||
|
|
||||||
|
export function ChatMessageContent() {
|
||||||
|
return (
|
||||||
|
<ChatMessage.Content>
|
||||||
|
<ChatMessage.Content.Event />
|
||||||
|
<ChatMessage.Content.AgentEvent />
|
||||||
|
<DeepResearchCard />
|
||||||
|
<ToolAnnotations />
|
||||||
|
<ChatMessage.Content.Image />
|
||||||
|
<ChatMessage.Content.Markdown />
|
||||||
|
<ChatMessage.Content.DocumentFile />
|
||||||
|
<ChatMessage.Content.Source />
|
||||||
|
<ChatMessage.Content.SuggestedQuestions />
|
||||||
|
</ChatMessage.Content>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { ChatMessage, ChatMessages, useChatUI } from "@llamaindex/chat-ui";
|
||||||
|
import { ChatMessageAvatar } from "./chat-avatar";
|
||||||
|
import { ChatMessageContent } from "./chat-message-content";
|
||||||
|
import { ChatStarter } from "./chat-starter";
|
||||||
|
|
||||||
|
export default function CustomChatMessages() {
|
||||||
|
const { messages } = useChatUI();
|
||||||
|
return (
|
||||||
|
<ChatMessages className="rounded-xl shadow-xl">
|
||||||
|
<ChatMessages.List>
|
||||||
|
{messages.map((message, index) => (
|
||||||
|
<ChatMessage
|
||||||
|
key={index}
|
||||||
|
message={message}
|
||||||
|
isLast={index === messages.length - 1}
|
||||||
|
>
|
||||||
|
<ChatMessageAvatar />
|
||||||
|
<ChatMessageContent />
|
||||||
|
<ChatMessage.Actions />
|
||||||
|
</ChatMessage>
|
||||||
|
))}
|
||||||
|
<ChatMessages.Loading />
|
||||||
|
</ChatMessages.List>
|
||||||
|
<ChatMessages.Actions />
|
||||||
|
<ChatStarter />
|
||||||
|
</ChatMessages>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
import { useChatUI } from "@llamaindex/chat-ui";
|
||||||
|
import { StarterQuestions } from "@llamaindex/chat-ui/widgets";
|
||||||
|
import { getConfig } from "../lib/utils";
|
||||||
|
|
||||||
|
export function ChatStarter() {
|
||||||
|
const { append, messages } = useChatUI();
|
||||||
|
const starterQuestions = getConfig("STARTER_QUESTIONS") ?? [];
|
||||||
|
|
||||||
|
if (starterQuestions.length === 0 || messages.length > 0) return null;
|
||||||
|
return <StarterQuestions append={append} questions={starterQuestions} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,209 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { getCustomAnnotation, useChatMessage } from "@llamaindex/chat-ui";
|
||||||
|
import {
|
||||||
|
AlertCircle,
|
||||||
|
CheckCircle2,
|
||||||
|
CircleDashed,
|
||||||
|
Clock,
|
||||||
|
NotebookPen,
|
||||||
|
Search,
|
||||||
|
} from "lucide-react";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import {
|
||||||
|
Accordion,
|
||||||
|
AccordionContent,
|
||||||
|
AccordionItem,
|
||||||
|
AccordionTrigger,
|
||||||
|
} from "../../accordion";
|
||||||
|
import { Card, CardContent, CardHeader, CardTitle } from "../../card";
|
||||||
|
import { cn } from "../../lib/utils";
|
||||||
|
import { Markdown } from "./markdown";
|
||||||
|
|
||||||
|
// Streaming event types
|
||||||
|
type EventState = "pending" | "inprogress" | "done" | "error";
|
||||||
|
|
||||||
|
type DeepResearchEvent = {
|
||||||
|
type: "deep_research_event";
|
||||||
|
data: {
|
||||||
|
event: "retrieve" | "analyze" | "answer";
|
||||||
|
state: EventState;
|
||||||
|
id?: string;
|
||||||
|
question?: string;
|
||||||
|
answer?: string | null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
// UI state types
|
||||||
|
type QuestionState = {
|
||||||
|
id: string;
|
||||||
|
question: string;
|
||||||
|
answer: string | null;
|
||||||
|
state: EventState;
|
||||||
|
isOpen: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
type DeepResearchCardState = {
|
||||||
|
retrieve: {
|
||||||
|
state: EventState | null;
|
||||||
|
};
|
||||||
|
analyze: {
|
||||||
|
state: EventState | null;
|
||||||
|
questions: QuestionState[];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
interface DeepResearchCardProps {
|
||||||
|
className?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stateIcon: Record<EventState, React.ReactNode> = {
|
||||||
|
pending: <Clock className="h-4 w-4 text-yellow-500" />,
|
||||||
|
inprogress: <CircleDashed className="h-4 w-4 animate-spin text-blue-500" />,
|
||||||
|
done: <CheckCircle2 className="h-4 w-4 text-green-500" />,
|
||||||
|
error: <AlertCircle className="h-4 w-4 text-red-500" />,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Transform the state based on the event without mutations
|
||||||
|
const transformState = (
|
||||||
|
state: DeepResearchCardState,
|
||||||
|
event: DeepResearchEvent,
|
||||||
|
): DeepResearchCardState => {
|
||||||
|
switch (event.data.event) {
|
||||||
|
case "answer": {
|
||||||
|
const { id, question, answer } = event.data;
|
||||||
|
if (!id || !question) return state;
|
||||||
|
|
||||||
|
const updatedQuestions = state.analyze.questions.map((q) => {
|
||||||
|
if (q.id !== id) return q;
|
||||||
|
return {
|
||||||
|
...q,
|
||||||
|
state: event.data.state,
|
||||||
|
answer: answer ?? q.answer,
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const newQuestion = !state.analyze.questions.some((q) => q.id === id)
|
||||||
|
? [
|
||||||
|
{
|
||||||
|
id,
|
||||||
|
question,
|
||||||
|
answer: answer ?? null,
|
||||||
|
state: event.data.state,
|
||||||
|
isOpen: false,
|
||||||
|
},
|
||||||
|
]
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
analyze: {
|
||||||
|
...state.analyze,
|
||||||
|
questions: [...updatedQuestions, ...newQuestion],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
case "retrieve":
|
||||||
|
case "analyze":
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
[event.data.event]: {
|
||||||
|
...state[event.data.event],
|
||||||
|
state: event.data.state,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
default:
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
// Convert deep research events to state
|
||||||
|
const deepResearchEventsToState = (
|
||||||
|
events: DeepResearchEvent[] | undefined,
|
||||||
|
): DeepResearchCardState => {
|
||||||
|
if (!events?.length) {
|
||||||
|
return {
|
||||||
|
retrieve: { state: null },
|
||||||
|
analyze: { state: null, questions: [] },
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: DeepResearchCardState = {
|
||||||
|
retrieve: { state: null },
|
||||||
|
analyze: { state: null, questions: [] },
|
||||||
|
};
|
||||||
|
|
||||||
|
return events.reduce(
|
||||||
|
(acc: DeepResearchCardState, event: DeepResearchEvent) =>
|
||||||
|
transformState(acc, event),
|
||||||
|
initialState,
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export function DeepResearchCard({ className }: DeepResearchCardProps) {
|
||||||
|
const { message } = useChatMessage();
|
||||||
|
|
||||||
|
const state = useMemo(() => {
|
||||||
|
const deepResearchEvents = getCustomAnnotation<DeepResearchEvent>(
|
||||||
|
message.annotations,
|
||||||
|
(annotation) => annotation?.type === "deep_research_event",
|
||||||
|
);
|
||||||
|
if (!deepResearchEvents.length) return null;
|
||||||
|
return deepResearchEventsToState(deepResearchEvents);
|
||||||
|
}, [message.annotations]);
|
||||||
|
|
||||||
|
if (!state) return null;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Card className={cn("w-full", className)}>
|
||||||
|
<CardHeader className="space-y-4">
|
||||||
|
{state.retrieve.state !== null && (
|
||||||
|
<CardTitle className="flex items-center gap-2">
|
||||||
|
<Search className="h-5 w-5" />
|
||||||
|
{state.retrieve.state === "inprogress"
|
||||||
|
? "Searching..."
|
||||||
|
: "Search completed"}
|
||||||
|
</CardTitle>
|
||||||
|
)}
|
||||||
|
{state.analyze.state !== null && (
|
||||||
|
<CardTitle className="flex items-center gap-2 border-t pt-4">
|
||||||
|
<NotebookPen className="h-5 w-5" />
|
||||||
|
{state.analyze.state === "inprogress" ? "Analyzing..." : "Analysis"}
|
||||||
|
</CardTitle>
|
||||||
|
)}
|
||||||
|
</CardHeader>
|
||||||
|
|
||||||
|
<CardContent>
|
||||||
|
{state.analyze.questions.length > 0 && (
|
||||||
|
<Accordion type="single" collapsible className="space-y-2">
|
||||||
|
{state.analyze.questions.map((question: QuestionState) => (
|
||||||
|
<AccordionItem
|
||||||
|
key={question.id}
|
||||||
|
value={question.id}
|
||||||
|
className="rounded-lg border [&[data-state=open]>div]:rounded-b-none"
|
||||||
|
>
|
||||||
|
<AccordionTrigger className="hover:bg-accent gap-2 px-3 py-3 hover:no-underline">
|
||||||
|
<div className="flex w-full items-center gap-2">
|
||||||
|
<div className="flex-shrink-0">
|
||||||
|
{stateIcon[question.state]}
|
||||||
|
</div>
|
||||||
|
<span className="flex-1 text-left font-medium">
|
||||||
|
{question.question}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</AccordionTrigger>
|
||||||
|
{question.answer && (
|
||||||
|
<AccordionContent className="border-t px-3 py-3">
|
||||||
|
<Markdown content={question.answer} />
|
||||||
|
</AccordionContent>
|
||||||
|
)}
|
||||||
|
</AccordionItem>
|
||||||
|
))}
|
||||||
|
</Accordion>
|
||||||
|
)}
|
||||||
|
</CardContent>
|
||||||
|
</Card>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,187 @@
|
|||||||
|
import { useChatUI } from "@llamaindex/chat-ui";
|
||||||
|
import { Loader2 } from "lucide-react";
|
||||||
|
import { useCallback, useEffect, useState } from "react";
|
||||||
|
import { getConfig } from "../../lib/utils";
|
||||||
|
import {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
} from "../../select";
|
||||||
|
|
||||||
|
type LLamaCloudPipeline = {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type LLamaCloudProject = {
|
||||||
|
id: string;
|
||||||
|
organization_id: string;
|
||||||
|
name: string;
|
||||||
|
is_default: boolean;
|
||||||
|
pipelines: Array<LLamaCloudPipeline>;
|
||||||
|
};
|
||||||
|
|
||||||
|
type PipelineConfig = {
|
||||||
|
project: string; // project name
|
||||||
|
pipeline: string; // pipeline name
|
||||||
|
};
|
||||||
|
|
||||||
|
type LlamaCloudConfig = {
|
||||||
|
projects?: LLamaCloudProject[];
|
||||||
|
pipeline?: PipelineConfig;
|
||||||
|
};
|
||||||
|
|
||||||
|
export interface LlamaCloudSelectorProps {
|
||||||
|
onSelect?: (pipeline: PipelineConfig | undefined) => void;
|
||||||
|
defaultPipeline?: PipelineConfig;
|
||||||
|
shouldCheckValid?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function LlamaCloudSelector({
|
||||||
|
onSelect,
|
||||||
|
defaultPipeline,
|
||||||
|
shouldCheckValid = false,
|
||||||
|
}: LlamaCloudSelectorProps) {
|
||||||
|
const { setRequestData } = useChatUI();
|
||||||
|
const [config, setConfig] = useState<LlamaCloudConfig>();
|
||||||
|
|
||||||
|
const updateRequestParams = useCallback(
|
||||||
|
(pipeline?: PipelineConfig) => {
|
||||||
|
if (setRequestData) {
|
||||||
|
setRequestData({
|
||||||
|
llamaCloudPipeline: pipeline,
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
onSelect?.(pipeline);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
[onSelect, setRequestData],
|
||||||
|
);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (process.env.NEXT_PUBLIC_USE_LLAMACLOUD === "true" && !config) {
|
||||||
|
fetch(getConfig("LLAMA_CLOUD_API"))
|
||||||
|
.then((response) => {
|
||||||
|
if (!response.ok) {
|
||||||
|
return response.json().then((errorData) => {
|
||||||
|
window.alert(
|
||||||
|
`Error: ${JSON.stringify(errorData) || "Unknown error occurred"}`,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
return response.json();
|
||||||
|
})
|
||||||
|
.then((data) => {
|
||||||
|
const pipeline = defaultPipeline ?? data.pipeline; // defaultPipeline will override pipeline in .env
|
||||||
|
setConfig({ ...data, pipeline });
|
||||||
|
updateRequestParams(pipeline);
|
||||||
|
})
|
||||||
|
.catch((error) => console.error("Error fetching config", error));
|
||||||
|
}
|
||||||
|
}, [config, defaultPipeline, updateRequestParams]);
|
||||||
|
|
||||||
|
const setPipeline = (pipelineConfig?: PipelineConfig) => {
|
||||||
|
setConfig((prevConfig) => ({
|
||||||
|
...prevConfig,
|
||||||
|
pipeline: pipelineConfig,
|
||||||
|
}));
|
||||||
|
updateRequestParams(pipelineConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePipelineSelect = async (value: string) => {
|
||||||
|
setPipeline(JSON.parse(value) as PipelineConfig);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (process.env.NEXT_PUBLIC_USE_LLAMACLOUD !== "true") {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config) {
|
||||||
|
return (
|
||||||
|
<div className="flex items-center justify-center p-3">
|
||||||
|
<Loader2 className="h-4 w-4 animate-spin" />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldCheckValid && !isValid(config.projects, config.pipeline)) {
|
||||||
|
return (
|
||||||
|
<p className="text-red-500">
|
||||||
|
Invalid LlamaCloud configuration. Check console logs.
|
||||||
|
</p>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
const { projects, pipeline } = config;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Select
|
||||||
|
onValueChange={handlePipelineSelect}
|
||||||
|
defaultValue={
|
||||||
|
isValid(projects, pipeline, false)
|
||||||
|
? JSON.stringify(pipeline)
|
||||||
|
: undefined
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<SelectTrigger className="w-[200px]">
|
||||||
|
<SelectValue placeholder="Select a pipeline" />
|
||||||
|
</SelectTrigger>
|
||||||
|
<SelectContent>
|
||||||
|
{projects!.map((project: LLamaCloudProject) => (
|
||||||
|
<SelectGroup key={project.id}>
|
||||||
|
<SelectLabel className="capitalize">
|
||||||
|
Project: {project.name}
|
||||||
|
</SelectLabel>
|
||||||
|
{project.pipelines.map((pipeline) => (
|
||||||
|
<SelectItem
|
||||||
|
key={pipeline.id}
|
||||||
|
className="last:border-b"
|
||||||
|
value={JSON.stringify({
|
||||||
|
pipeline: pipeline.name,
|
||||||
|
project: project.name,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<span className="pl-2">{pipeline.name}</span>
|
||||||
|
</SelectItem>
|
||||||
|
))}
|
||||||
|
</SelectGroup>
|
||||||
|
))}
|
||||||
|
</SelectContent>
|
||||||
|
</Select>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValid(
|
||||||
|
projects: LLamaCloudProject[] | undefined,
|
||||||
|
pipeline: PipelineConfig | undefined,
|
||||||
|
logErrors: boolean = true,
|
||||||
|
): boolean {
|
||||||
|
if (!projects?.length) return false;
|
||||||
|
if (!pipeline) return false;
|
||||||
|
const matchedProject = projects.find(
|
||||||
|
(project: LLamaCloudProject) => project.name === pipeline.project,
|
||||||
|
);
|
||||||
|
if (!matchedProject) {
|
||||||
|
if (logErrors) {
|
||||||
|
console.error(
|
||||||
|
`LlamaCloud project ${pipeline.project} not found. Check LLAMA_CLOUD_PROJECT_NAME variable`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const pipelineExists = matchedProject.pipelines.some(
|
||||||
|
(p) => p.name === pipeline.pipeline,
|
||||||
|
);
|
||||||
|
if (!pipelineExists) {
|
||||||
|
if (logErrors) {
|
||||||
|
console.error(
|
||||||
|
`LlamaCloud pipeline ${pipeline.pipeline} not found. Check LLAMA_CLOUD_INDEX_NAME variable`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import { SourceData } from "@llamaindex/chat-ui";
|
||||||
|
import { Markdown as MarkdownUI } from "@llamaindex/chat-ui/widgets";
|
||||||
|
import { getConfig } from "../../lib/utils";
|
||||||
|
const preprocessMedia = (content: string) => {
|
||||||
|
// Remove `sandbox:` from the beginning of the URL before rendering markdown
|
||||||
|
// OpenAI models sometimes prepend `sandbox:` to relative URLs - this fixes it
|
||||||
|
return content.replace(/(sandbox|attachment|snt):/g, "");
|
||||||
|
};
|
||||||
|
|
||||||
|
export function Markdown({
|
||||||
|
content,
|
||||||
|
sources,
|
||||||
|
}: {
|
||||||
|
content: string;
|
||||||
|
sources?: SourceData;
|
||||||
|
}) {
|
||||||
|
const processedContent = preprocessMedia(content);
|
||||||
|
return (
|
||||||
|
<MarkdownUI
|
||||||
|
content={processedContent}
|
||||||
|
backend={getConfig("BACKEND")}
|
||||||
|
sources={sources}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as React from "react";
|
||||||
|
|
||||||
|
export interface useCopyToClipboardProps {
|
||||||
|
timeout?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function useCopyToClipboard({
|
||||||
|
timeout = 2000,
|
||||||
|
}: useCopyToClipboardProps) {
|
||||||
|
const [isCopied, setIsCopied] = React.useState(false);
|
||||||
|
|
||||||
|
const copyToClipboard = (value: string) => {
|
||||||
|
if (typeof window === "undefined" || !navigator.clipboard?.writeText) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
navigator.clipboard.writeText(value).then(() => {
|
||||||
|
setIsCopied(true);
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
setIsCopied(false);
|
||||||
|
}, timeout);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
return { isCopied, copyToClipboard };
|
||||||
|
}
|
||||||
@@ -0,0 +1,96 @@
|
|||||||
|
import {
|
||||||
|
Message,
|
||||||
|
MessageAnnotation,
|
||||||
|
getChatUIAnnotation,
|
||||||
|
useChatMessage,
|
||||||
|
useChatUI,
|
||||||
|
} from "@llamaindex/chat-ui";
|
||||||
|
import { JSONValue } from "ai";
|
||||||
|
import { useMemo } from "react";
|
||||||
|
import { WeatherCard, WeatherData } from "./weather-card";
|
||||||
|
|
||||||
|
export function ToolAnnotations() {
|
||||||
|
// TODO: This is a bit of a hack to get the artifact version. better to generate the version in the tool call and
|
||||||
|
// store it in CodeArtifact
|
||||||
|
const { messages } = useChatUI();
|
||||||
|
const { message } = useChatMessage();
|
||||||
|
const artifactVersion = useMemo(
|
||||||
|
() => getArtifactVersion(messages, message),
|
||||||
|
[messages, message],
|
||||||
|
);
|
||||||
|
// Get the tool data from the message annotations
|
||||||
|
const annotations = message.annotations as MessageAnnotation[] | undefined;
|
||||||
|
const toolData = annotations
|
||||||
|
? (getChatUIAnnotation(annotations, "tools") as unknown as ToolData[])
|
||||||
|
: null;
|
||||||
|
return toolData?.[0] ? (
|
||||||
|
<ChatTools data={toolData[0]} artifactVersion={artifactVersion} />
|
||||||
|
) : null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Used to render outputs of tools. If needed, add more renderers here.
|
||||||
|
function ChatTools({
|
||||||
|
data,
|
||||||
|
artifactVersion,
|
||||||
|
}: {
|
||||||
|
data: ToolData;
|
||||||
|
artifactVersion: number | undefined;
|
||||||
|
}) {
|
||||||
|
if (!data) return null;
|
||||||
|
const { toolCall, toolOutput } = data;
|
||||||
|
|
||||||
|
if (toolOutput.isError) {
|
||||||
|
return (
|
||||||
|
<div className="border-l-2 border-red-400 pl-2">
|
||||||
|
There was an error when calling the tool {toolCall.name} with input:{" "}
|
||||||
|
<br />
|
||||||
|
{JSON.stringify(toolCall.input)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (toolCall.name) {
|
||||||
|
case "get_weather_information": {
|
||||||
|
const weatherData = toolOutput.output as unknown as WeatherData;
|
||||||
|
return <WeatherCard data={weatherData} />;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type ToolData = {
|
||||||
|
toolCall: {
|
||||||
|
id: string;
|
||||||
|
name: string;
|
||||||
|
input: {
|
||||||
|
[key: string]: JSONValue;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
toolOutput: {
|
||||||
|
output: JSONValue;
|
||||||
|
isError: boolean;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
function getArtifactVersion(
|
||||||
|
messages: Message[],
|
||||||
|
message: Message,
|
||||||
|
): number | undefined {
|
||||||
|
const messageId = "id" in message ? message.id : undefined;
|
||||||
|
if (!messageId) return undefined;
|
||||||
|
let versionIndex = 1;
|
||||||
|
for (const m of messages) {
|
||||||
|
const toolData = m.annotations
|
||||||
|
? (getChatUIAnnotation(m.annotations, "tools") as unknown as ToolData[])
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (toolData?.some((t) => t.toolCall.name === "artifact")) {
|
||||||
|
if ("id" in m && m.id === messageId) {
|
||||||
|
return versionIndex;
|
||||||
|
}
|
||||||
|
versionIndex++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
export interface WeatherData {
|
||||||
|
latitude: number;
|
||||||
|
longitude: number;
|
||||||
|
generationtime_ms: number;
|
||||||
|
utc_offset_seconds: number;
|
||||||
|
timezone: string;
|
||||||
|
timezone_abbreviation: string;
|
||||||
|
elevation: number;
|
||||||
|
current_units: {
|
||||||
|
time: string;
|
||||||
|
interval: string;
|
||||||
|
temperature_2m: string;
|
||||||
|
weather_code: string;
|
||||||
|
};
|
||||||
|
current: {
|
||||||
|
time: string;
|
||||||
|
interval: number;
|
||||||
|
temperature_2m: number;
|
||||||
|
weather_code: number;
|
||||||
|
};
|
||||||
|
hourly_units: {
|
||||||
|
time: string;
|
||||||
|
temperature_2m: string;
|
||||||
|
weather_code: string;
|
||||||
|
};
|
||||||
|
hourly: {
|
||||||
|
time: string[];
|
||||||
|
temperature_2m: number[];
|
||||||
|
weather_code: number[];
|
||||||
|
};
|
||||||
|
daily_units: {
|
||||||
|
time: string;
|
||||||
|
weather_code: string;
|
||||||
|
};
|
||||||
|
daily: {
|
||||||
|
time: string[];
|
||||||
|
weather_code: number[];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// Follow WMO Weather interpretation codes (WW)
|
||||||
|
const weatherCodeDisplayMap: Record<
|
||||||
|
string,
|
||||||
|
{
|
||||||
|
icon: React.ReactNode;
|
||||||
|
status: string;
|
||||||
|
}
|
||||||
|
> = {
|
||||||
|
"0": {
|
||||||
|
icon: <span>☀️</span>,
|
||||||
|
status: "Clear sky",
|
||||||
|
},
|
||||||
|
"1": {
|
||||||
|
icon: <span>🌤️</span>,
|
||||||
|
status: "Mainly clear",
|
||||||
|
},
|
||||||
|
"2": {
|
||||||
|
icon: <span>☁️</span>,
|
||||||
|
status: "Partly cloudy",
|
||||||
|
},
|
||||||
|
"3": {
|
||||||
|
icon: <span>☁️</span>,
|
||||||
|
status: "Overcast",
|
||||||
|
},
|
||||||
|
"45": {
|
||||||
|
icon: <span>🌫️</span>,
|
||||||
|
status: "Fog",
|
||||||
|
},
|
||||||
|
"48": {
|
||||||
|
icon: <span>🌫️</span>,
|
||||||
|
status: "Depositing rime fog",
|
||||||
|
},
|
||||||
|
"51": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Drizzle",
|
||||||
|
},
|
||||||
|
"53": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Drizzle",
|
||||||
|
},
|
||||||
|
"55": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Drizzle",
|
||||||
|
},
|
||||||
|
"56": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Freezing Drizzle",
|
||||||
|
},
|
||||||
|
"57": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Freezing Drizzle",
|
||||||
|
},
|
||||||
|
"61": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain",
|
||||||
|
},
|
||||||
|
"63": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain",
|
||||||
|
},
|
||||||
|
"65": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain",
|
||||||
|
},
|
||||||
|
"66": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Freezing Rain",
|
||||||
|
},
|
||||||
|
"67": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Freezing Rain",
|
||||||
|
},
|
||||||
|
"71": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow fall",
|
||||||
|
},
|
||||||
|
"73": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow fall",
|
||||||
|
},
|
||||||
|
"75": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow fall",
|
||||||
|
},
|
||||||
|
"77": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow grains",
|
||||||
|
},
|
||||||
|
"80": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain showers",
|
||||||
|
},
|
||||||
|
"81": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain showers",
|
||||||
|
},
|
||||||
|
"82": {
|
||||||
|
icon: <span>🌧️</span>,
|
||||||
|
status: "Rain showers",
|
||||||
|
},
|
||||||
|
"85": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow showers",
|
||||||
|
},
|
||||||
|
"86": {
|
||||||
|
icon: <span>❄️</span>,
|
||||||
|
status: "Snow showers",
|
||||||
|
},
|
||||||
|
"95": {
|
||||||
|
icon: <span>⛈️</span>,
|
||||||
|
status: "Thunderstorm",
|
||||||
|
},
|
||||||
|
"96": {
|
||||||
|
icon: <span>⛈️</span>,
|
||||||
|
status: "Thunderstorm",
|
||||||
|
},
|
||||||
|
"99": {
|
||||||
|
icon: <span>⛈️</span>,
|
||||||
|
status: "Thunderstorm",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const displayDay = (time: string) => {
|
||||||
|
return new Date(time).toLocaleDateString("en-US", {
|
||||||
|
weekday: "long",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export function WeatherCard({ data }: { data: WeatherData }) {
|
||||||
|
const currentDayString = new Date(data.current.time).toLocaleDateString(
|
||||||
|
"en-US",
|
||||||
|
{
|
||||||
|
weekday: "long",
|
||||||
|
month: "long",
|
||||||
|
day: "numeric",
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-fit space-y-4 rounded-2xl bg-[#61B9F2] p-5 text-white shadow-xl">
|
||||||
|
<div className="flex justify-between">
|
||||||
|
<div className="space-y-2">
|
||||||
|
<div className="text-xl">{currentDayString}</div>
|
||||||
|
<div className="flex gap-4 text-5xl font-semibold">
|
||||||
|
<span>
|
||||||
|
{data.current.temperature_2m} {data.current_units.temperature_2m}
|
||||||
|
</span>
|
||||||
|
{weatherCodeDisplayMap[data.current.weather_code].icon}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<span className="text-xl">
|
||||||
|
{weatherCodeDisplayMap[data.current.weather_code].status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div className="grid grid-cols-6 gap-2">
|
||||||
|
{data.daily.time.map((time, index) => {
|
||||||
|
if (index === 0) return null; // skip the current day
|
||||||
|
return (
|
||||||
|
<div key={time} className="flex flex-col items-center gap-4">
|
||||||
|
<span>{displayDay(time)}</span>
|
||||||
|
<div className="text-4xl">
|
||||||
|
{weatherCodeDisplayMap[data.daily.weather_code[index]].icon}
|
||||||
|
</div>
|
||||||
|
<span className="text-sm">
|
||||||
|
{weatherCodeDisplayMap[data.daily.weather_code[index]].status}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as CollapsiblePrimitive from "@radix-ui/react-collapsible";
|
||||||
|
|
||||||
|
const Collapsible = CollapsiblePrimitive.Root;
|
||||||
|
|
||||||
|
const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
|
||||||
|
|
||||||
|
const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
|
||||||
|
|
||||||
|
export { Collapsible, CollapsibleContent, CollapsibleTrigger };
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import { clsx, type ClassValue } from "clsx";
|
||||||
|
import { twMerge } from "tailwind-merge";
|
||||||
|
|
||||||
|
export function cn(...inputs: ClassValue[]) {
|
||||||
|
return twMerge(clsx(inputs));
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getConfig = (name: string) => {
|
||||||
|
if (typeof window === "undefined") return undefined;
|
||||||
|
return (window as any).LLAMAINDEX?.[name];
|
||||||
|
};
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as SelectPrimitive from "@radix-ui/react-select";
|
||||||
|
import { Check, ChevronDown, ChevronUp } from "lucide-react";
|
||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const Select = SelectPrimitive.Root;
|
||||||
|
|
||||||
|
const SelectGroup = SelectPrimitive.Group;
|
||||||
|
|
||||||
|
const SelectValue = SelectPrimitive.Value;
|
||||||
|
|
||||||
|
const SelectTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"border-input bg-background ring-offset-background placeholder:text-muted-foreground focus:outline-hidden focus:ring-ring flex h-10 w-full items-center justify-between rounded-md border px-3 py-2 text-sm focus:ring-2 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
<SelectPrimitive.Icon asChild>
|
||||||
|
<ChevronDown className="h-4 w-4 opacity-50" />
|
||||||
|
</SelectPrimitive.Icon>
|
||||||
|
</SelectPrimitive.Trigger>
|
||||||
|
));
|
||||||
|
SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
|
const SelectScrollUpButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollUpButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronUp className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ScrollUpButton>
|
||||||
|
));
|
||||||
|
SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
|
||||||
|
|
||||||
|
const SelectScrollDownButton = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.ScrollDownButton
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"flex cursor-default items-center justify-center py-1",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<ChevronDown className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ScrollDownButton>
|
||||||
|
));
|
||||||
|
SelectScrollDownButton.displayName =
|
||||||
|
SelectPrimitive.ScrollDownButton.displayName;
|
||||||
|
|
||||||
|
const SelectContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
|
||||||
|
>(({ className, children, position = "popper", ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Portal>
|
||||||
|
<SelectPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md border shadow-md",
|
||||||
|
position === "popper" &&
|
||||||
|
"data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
position={position}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<SelectScrollUpButton />
|
||||||
|
<SelectPrimitive.Viewport
|
||||||
|
className={cn(
|
||||||
|
"p-1",
|
||||||
|
position === "popper" &&
|
||||||
|
"h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</SelectPrimitive.Viewport>
|
||||||
|
<SelectScrollDownButton />
|
||||||
|
</SelectPrimitive.Content>
|
||||||
|
</SelectPrimitive.Portal>
|
||||||
|
));
|
||||||
|
SelectContent.displayName = SelectPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
const SelectLabel = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Label>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Label
|
||||||
|
ref={ref}
|
||||||
|
className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectLabel.displayName = SelectPrimitive.Label.displayName;
|
||||||
|
|
||||||
|
const SelectItem = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Item>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
|
||||||
|
>(({ className, children, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Item
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"outline-hidden focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
>
|
||||||
|
<span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
|
||||||
|
<SelectPrimitive.ItemIndicator>
|
||||||
|
<Check className="h-4 w-4" />
|
||||||
|
</SelectPrimitive.ItemIndicator>
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
|
||||||
|
</SelectPrimitive.Item>
|
||||||
|
));
|
||||||
|
SelectItem.displayName = SelectPrimitive.Item.displayName;
|
||||||
|
|
||||||
|
const SelectSeparator = React.forwardRef<
|
||||||
|
React.ElementRef<typeof SelectPrimitive.Separator>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<SelectPrimitive.Separator
|
||||||
|
ref={ref}
|
||||||
|
className={cn("bg-muted -mx-1 my-1 h-px", className)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
|
||||||
|
|
||||||
|
export {
|
||||||
|
Select,
|
||||||
|
SelectContent,
|
||||||
|
SelectGroup,
|
||||||
|
SelectItem,
|
||||||
|
SelectLabel,
|
||||||
|
SelectScrollDownButton,
|
||||||
|
SelectScrollUpButton,
|
||||||
|
SelectSeparator,
|
||||||
|
SelectTrigger,
|
||||||
|
SelectValue,
|
||||||
|
};
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import * as TabsPrimitive from "@radix-ui/react-tabs";
|
||||||
|
import * as React from "react";
|
||||||
|
import { cn } from "./lib/utils";
|
||||||
|
|
||||||
|
const Tabs = TabsPrimitive.Root;
|
||||||
|
|
||||||
|
const TabsList = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.List>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.List>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.List
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"bg-muted text-muted-foreground inline-flex h-9 items-center justify-center rounded-lg p-1",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
TabsList.displayName = TabsPrimitive.List.displayName;
|
||||||
|
|
||||||
|
const TabsTrigger = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Trigger>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Trigger>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Trigger
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"ring-offset-background focus-visible:outline-hidden focus-visible:ring-ring data-[state=active]:bg-background data-[state=active]:text-foreground inline-flex items-center justify-center whitespace-nowrap rounded-md px-3 py-1 text-sm font-medium transition-all focus-visible:ring-2 focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:shadow-sm",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
|
||||||
|
|
||||||
|
const TabsContent = React.forwardRef<
|
||||||
|
React.ElementRef<typeof TabsPrimitive.Content>,
|
||||||
|
React.ComponentPropsWithoutRef<typeof TabsPrimitive.Content>
|
||||||
|
>(({ className, ...props }, ref) => (
|
||||||
|
<TabsPrimitive.Content
|
||||||
|
ref={ref}
|
||||||
|
className={cn(
|
||||||
|
"ring-offset-background focus-visible:outline-hidden focus-visible:ring-ring focus-visible:ring-2 focus-visible:ring-offset-2",
|
||||||
|
className,
|
||||||
|
)}
|
||||||
|
{...props}
|
||||||
|
/>
|
||||||
|
));
|
||||||
|
TabsContent.displayName = TabsPrimitive.Content.displayName;
|
||||||
|
|
||||||
|
export { Tabs, TabsContent, TabsList, TabsTrigger };
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 15 KiB |
@@ -45,12 +45,6 @@
|
|||||||
--animate-accordion-down: accordion-down 0.2s ease-out;
|
--animate-accordion-down: accordion-down 0.2s ease-out;
|
||||||
--animate-accordion-up: accordion-up 0.2s ease-out;
|
--animate-accordion-up: accordion-up 0.2s ease-out;
|
||||||
|
|
||||||
--background-image-glow-conic:
|
|
||||||
radial-gradient(at 21% 11%, rgba(186, 186, 233, 0.53) 0, transparent 50%),
|
|
||||||
radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
|
|
||||||
radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
|
|
||||||
radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
|
|
||||||
|
|
||||||
@keyframes accordion-down {
|
@keyframes accordion-down {
|
||||||
from {
|
from {
|
||||||
height: 0;
|
height: 0;
|
||||||
@@ -69,6 +63,17 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@utility container {
|
||||||
|
margin-inline: auto;
|
||||||
|
padding-inline: 2rem;
|
||||||
|
@media (width >= --theme(--breakpoint-sm)) {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
@media (width >= 1400px) {
|
||||||
|
max-width: 1400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
The default border color has changed to `currentColor` in Tailwind CSS v4,
|
||||||
so we've added these compatibility styles to make sure everything still
|
so we've added these compatibility styles to make sure everything still
|
||||||
@@ -90,57 +95,68 @@
|
|||||||
@layer base {
|
@layer base {
|
||||||
:root {
|
:root {
|
||||||
--background: 0 0% 100%;
|
--background: 0 0% 100%;
|
||||||
--foreground: 240 10% 3.9%;
|
--foreground: 222.2 47.4% 11.2%;
|
||||||
--card: 0 0% 100%;
|
|
||||||
--card-foreground: 240 10% 3.9%;
|
--muted: 210 40% 96.1%;
|
||||||
|
--muted-foreground: 215.4 16.3% 46.9%;
|
||||||
|
|
||||||
--popover: 0 0% 100%;
|
--popover: 0 0% 100%;
|
||||||
--popover-foreground: 240 10% 3.9%;
|
--popover-foreground: 222.2 47.4% 11.2%;
|
||||||
--primary: 240 5.9% 10%;
|
|
||||||
--primary-foreground: 0 0% 98%;
|
--border: 214.3 31.8% 91.4%;
|
||||||
--secondary: 240 4.8% 95.9%;
|
--input: 214.3 31.8% 91.4%;
|
||||||
--secondary-foreground: 240 5.9% 10%;
|
|
||||||
--muted: 240 4.8% 95.9%;
|
--card: 0 0% 100%;
|
||||||
--muted-foreground: 240 3.8% 46.1%;
|
--card-foreground: 222.2 47.4% 11.2%;
|
||||||
--accent: 240 4.8% 95.9%;
|
|
||||||
--accent-foreground: 240 5.9% 10%;
|
--primary: 222.2 47.4% 11.2%;
|
||||||
--destructive: 0 84.2% 60.2%;
|
--primary-foreground: 210 40% 98%;
|
||||||
--destructive-foreground: 0 0% 98%;
|
|
||||||
--border: 240 5.9% 90%;
|
--secondary: 210 40% 96.1%;
|
||||||
--input: 240 5.9% 90%;
|
--secondary-foreground: 222.2 47.4% 11.2%;
|
||||||
--ring: 240 10% 3.9%;
|
|
||||||
|
--accent: 210 40% 96.1%;
|
||||||
|
--accent-foreground: 222.2 47.4% 11.2%;
|
||||||
|
|
||||||
|
--destructive: 0 100% 50%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 215 20.2% 65.1%;
|
||||||
|
|
||||||
--radius: 0.5rem;
|
--radius: 0.5rem;
|
||||||
--chart-1: 12 76% 61%;
|
|
||||||
--chart-2: 173 58% 39%;
|
|
||||||
--chart-3: 197 37% 24%;
|
|
||||||
--chart-4: 43 74% 66%;
|
|
||||||
--chart-5: 27 87% 67%;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.dark {
|
.dark {
|
||||||
--background: 240 10% 3.9%;
|
--background: 224 71% 4%;
|
||||||
--foreground: 0 0% 98%;
|
--foreground: 213 31% 91%;
|
||||||
--card: 240 10% 3.9%;
|
|
||||||
--card-foreground: 0 0% 98%;
|
--muted: 223 47% 11%;
|
||||||
--popover: 240 10% 3.9%;
|
--muted-foreground: 215.4 16.3% 56.9%;
|
||||||
--popover-foreground: 0 0% 98%;
|
|
||||||
--primary: 0 0% 98%;
|
--accent: 216 34% 17%;
|
||||||
--primary-foreground: 240 5.9% 10%;
|
--accent-foreground: 210 40% 98%;
|
||||||
--secondary: 240 3.7% 15.9%;
|
|
||||||
--secondary-foreground: 0 0% 98%;
|
--popover: 224 71% 4%;
|
||||||
--muted: 240 3.7% 15.9%;
|
--popover-foreground: 215 20.2% 65.1%;
|
||||||
--muted-foreground: 240 5% 64.9%;
|
|
||||||
--accent: 240 3.7% 15.9%;
|
--border: 216 34% 17%;
|
||||||
--accent-foreground: 0 0% 98%;
|
--input: 216 34% 17%;
|
||||||
--destructive: 0 62.8% 30.6%;
|
|
||||||
--destructive-foreground: 0 0% 98%;
|
--card: 224 71% 4%;
|
||||||
--border: 240 3.7% 15.9%;
|
--card-foreground: 213 31% 91%;
|
||||||
--input: 240 3.7% 15.9%;
|
|
||||||
--ring: 240 4.9% 83.9%;
|
--primary: 210 40% 98%;
|
||||||
--chart-1: 220 70% 50%;
|
--primary-foreground: 222.2 47.4% 1.2%;
|
||||||
--chart-2: 160 60% 45%;
|
|
||||||
--chart-3: 30 80% 55%;
|
--secondary: 222.2 47.4% 11.2%;
|
||||||
--chart-4: 280 65% 60%;
|
--secondary-foreground: 210 40% 98%;
|
||||||
--chart-5: 340 75% 55%;
|
|
||||||
|
--destructive: 0 63% 31%;
|
||||||
|
--destructive-foreground: 210 40% 98%;
|
||||||
|
|
||||||
|
--ring: 216 34% 17%;
|
||||||
|
|
||||||
|
--radius: 0.5rem;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,7 +164,50 @@
|
|||||||
* {
|
* {
|
||||||
@apply border-border;
|
@apply border-border;
|
||||||
}
|
}
|
||||||
|
html {
|
||||||
|
@apply h-full;
|
||||||
|
}
|
||||||
body {
|
body {
|
||||||
@apply bg-background text-foreground;
|
@apply bg-background text-foreground h-full;
|
||||||
|
font-feature-settings:
|
||||||
|
"rlig" 1,
|
||||||
|
"calt" 1;
|
||||||
|
}
|
||||||
|
.background-gradient {
|
||||||
|
background-color: #fff;
|
||||||
|
background-image:
|
||||||
|
radial-gradient(at 21% 11%, rgba(186, 186, 233, 0.53) 0, transparent 50%),
|
||||||
|
radial-gradient(at 85% 0, hsla(46, 57%, 78%, 0.52) 0, transparent 50%),
|
||||||
|
radial-gradient(at 91% 36%, rgba(194, 213, 255, 0.68) 0, transparent 50%),
|
||||||
|
radial-gradient(at 8% 40%, rgba(251, 218, 239, 0.46) 0, transparent 50%);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes fadeIn {
|
||||||
|
0% {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.fadein-agent {
|
||||||
|
animation-name: fadeIn;
|
||||||
|
animation-duration: 1.5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideIn {
|
||||||
|
from {
|
||||||
|
transform: translateX(10%);
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
transform: translateX(0);
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.animate-slideIn {
|
||||||
|
animation: slideIn 0.5s ease-out;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +1,22 @@
|
|||||||
import type { Metadata } from "next";
|
import type { Metadata } from "next";
|
||||||
|
import { Inter } from "next/font/google";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
|
|
||||||
|
const inter = Inter({ subsets: ["latin"] });
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "LlamaIndex Server",
|
title: "Create Llama App",
|
||||||
description: "LlamaIndex Server UI",
|
description: "Generated by create-llama",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: {
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<body>{children}</body>
|
<body className={inter.className}>{children}</body>
|
||||||
<script async src="./config.js"></script>
|
<script async src="./config.js"></script>
|
||||||
</html>
|
</html>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,29 +1,27 @@
|
|||||||
"use client";
|
"use client";
|
||||||
import { ChatSection } from "@llamaindex/chat-ui";
|
|
||||||
import { useChat } from "ai/react";
|
|
||||||
import { getConfig } from "./utils";
|
|
||||||
|
|
||||||
export default function Page() {
|
import Header from "@/app/components/header";
|
||||||
const handler = useChat({
|
import { Loader2 } from "lucide-react";
|
||||||
api: getConfig("CHAT_API"),
|
import dynamic from "next/dynamic";
|
||||||
onError: (error: unknown) => {
|
|
||||||
if (!(error instanceof Error)) throw error;
|
const ChatSection = dynamic(() => import("./components/chat-section"), {
|
||||||
let errorMessage: string;
|
ssr: false,
|
||||||
try {
|
loading: () => (
|
||||||
errorMessage = JSON.parse(error.message).detail;
|
<div className="flex h-full w-full items-center justify-center">
|
||||||
} catch (e) {
|
<Loader2 className="text-muted-foreground animate-spin" />
|
||||||
console.error(e);
|
|
||||||
errorMessage = error.message;
|
|
||||||
}
|
|
||||||
alert(errorMessage);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
return (
|
|
||||||
<div className="flex h-screen items-center justify-center">
|
|
||||||
<ChatSection
|
|
||||||
className="h-[72vh] w-[72vw] rounded-2xl shadow-2xl"
|
|
||||||
handler={handler}
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
return (
|
||||||
|
<main className="background-gradient flex h-screen w-screen items-center justify-center">
|
||||||
|
<div className="w-[90%] space-y-2 lg:w-[60rem] lg:space-y-10">
|
||||||
|
<Header />
|
||||||
|
<div className="flex h-[65vh]">
|
||||||
|
<ChatSection />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</main>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
||||||
"use client";
|
|
||||||
|
|
||||||
export const getConfig = (name: string) => {
|
|
||||||
if (typeof window === "undefined") return undefined;
|
|
||||||
return (window as any).LLAMAINDEX?.[name];
|
|
||||||
};
|
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"$schema": "https://ui.shadcn.com/schema.json",
|
||||||
|
"style": "default",
|
||||||
|
"rsc": true,
|
||||||
|
"tsx": true,
|
||||||
|
"tailwind": {
|
||||||
|
"config": "tailwind.config.ts",
|
||||||
|
"css": "app/globals.css",
|
||||||
|
"baseColor": "slate",
|
||||||
|
"cssVariables": true,
|
||||||
|
"prefix": ""
|
||||||
|
},
|
||||||
|
"aliases": {
|
||||||
|
"components": "@/app/components",
|
||||||
|
"utils": "@/app/components/ui/lib/utils",
|
||||||
|
"ui": "@/app/components/ui",
|
||||||
|
"lib": "@/app/components/ui/lib",
|
||||||
|
"hooks": "@/app/components/ui/hooks"
|
||||||
|
},
|
||||||
|
"iconLibrary": "lucide"
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import { dirname } from "path";
|
|
||||||
import { fileURLToPath } from "url";
|
|
||||||
import { FlatCompat } from "@eslint/eslintrc";
|
|
||||||
|
|
||||||
const __filename = fileURLToPath(import.meta.url);
|
|
||||||
const __dirname = dirname(__filename);
|
|
||||||
|
|
||||||
const compat = new FlatCompat({
|
|
||||||
baseDirectory: __dirname,
|
|
||||||
});
|
|
||||||
|
|
||||||
const eslintConfig = [
|
|
||||||
...compat.extends("next/core-web-vitals", "next/typescript"),
|
|
||||||
];
|
|
||||||
|
|
||||||
export default eslintConfig;
|
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
export default {
|
||||||
|
plugins: {
|
||||||
|
"@tailwindcss/postcss": {},
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
const config = {
|
|
||||||
plugins: ["@tailwindcss/postcss"],
|
|
||||||
};
|
|
||||||
|
|
||||||
export default config;
|
|
||||||
@@ -2,4 +2,8 @@
|
|||||||
|
|
||||||
window.LLAMAINDEX = {
|
window.LLAMAINDEX = {
|
||||||
CHAT_API: "/api/chat",
|
CHAT_API: "/api/chat",
|
||||||
|
// BACKEND: "http://localhost:3000",
|
||||||
|
// UPLOAD_API: "/api/chat/upload",
|
||||||
|
// LLAMA_CLOUD_API: "/api/chat/config/llamacloud",
|
||||||
|
// STARTER_QUESTIONS: [],
|
||||||
};
|
};
|
||||||
|
|||||||
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"target": "ES2017",
|
|
||||||
"lib": ["dom", "dom.iterable", "esnext"],
|
"lib": ["dom", "dom.iterable", "esnext"],
|
||||||
"allowJs": true,
|
"allowJs": true,
|
||||||
"skipLibCheck": true,
|
"skipLibCheck": true,
|
||||||
@@ -20,14 +19,9 @@
|
|||||||
],
|
],
|
||||||
"paths": {
|
"paths": {
|
||||||
"@/*": ["./*"]
|
"@/*": ["./*"]
|
||||||
}
|
},
|
||||||
|
"target": "ES2017"
|
||||||
},
|
},
|
||||||
"include": [
|
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
|
||||||
"**/*.ts",
|
|
||||||
"**/*.tsx",
|
|
||||||
".next/types/**/*.ts",
|
|
||||||
"next-env.d.ts",
|
|
||||||
"../.next/types/**/*.ts"
|
|
||||||
],
|
|
||||||
"exclude": ["node_modules"]
|
"exclude": ["node_modules"]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/server",
|
"name": "@llamaindex/server",
|
||||||
"description": "LlamaIndex Server",
|
"description": "LlamaIndex Server",
|
||||||
"version": "0.0.2",
|
"version": "0.0.3",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"main": "./dist/index.cjs",
|
"main": "./dist/index.cjs",
|
||||||
"module": "./dist/index.js",
|
"module": "./dist/index.js",
|
||||||
@@ -50,12 +50,20 @@
|
|||||||
"tsx": "^4.19.3"
|
"tsx": "^4.19.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@llamaindex/workflow": "workspace:*",
|
|
||||||
"@llamaindex/core": "workspace:*",
|
|
||||||
"@llamaindex/chat-ui": "0.3.1",
|
|
||||||
"ai": "^4.2.0",
|
"ai": "^4.2.0",
|
||||||
|
"llamaindex": "workspace:*",
|
||||||
|
"next": "15.2.3",
|
||||||
"react": "^19.0.0",
|
"react": "^19.0.0",
|
||||||
"react-dom": "^19.0.0",
|
"react-dom": "^19.0.0",
|
||||||
"next": "15.2.3"
|
"@radix-ui/react-tabs": "^1.1.0",
|
||||||
|
"@radix-ui/react-accordion": "^1.2.2",
|
||||||
|
"@radix-ui/react-collapsible": "^1.0.3",
|
||||||
|
"@radix-ui/react-select": "^2.1.1",
|
||||||
|
"@radix-ui/react-slot": "^1.0.2",
|
||||||
|
"@llamaindex/chat-ui": "0.3.2",
|
||||||
|
"class-variance-authority": "^0.7.1",
|
||||||
|
"tailwind-merge": "^2.6.0",
|
||||||
|
"clsx": "^2.1.1",
|
||||||
|
"lucide-react": "^0.460.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import type { ChatMessage } from "@llamaindex/core/llms";
|
|
||||||
import { type Message } from "ai";
|
import { type Message } from "ai";
|
||||||
import { IncomingMessage, ServerResponse } from "http";
|
import { IncomingMessage, ServerResponse } from "http";
|
||||||
|
import type { ChatMessage } from "llamaindex";
|
||||||
import type { WorkflowFactory } from "../types";
|
import type { WorkflowFactory } from "../types";
|
||||||
import {
|
import {
|
||||||
parseRequestBody,
|
parseRequestBody,
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import fs from "fs";
|
||||||
import { createServer } from "http";
|
import { createServer } from "http";
|
||||||
import next from "next";
|
import next from "next";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
@@ -6,17 +7,34 @@ import { handleChat } from "./handlers/chat";
|
|||||||
import { handleServeFiles } from "./handlers/files";
|
import { handleServeFiles } from "./handlers/files";
|
||||||
import type { LlamaIndexServerOptions, ServerWorkflow } from "./types";
|
import type { LlamaIndexServerOptions, ServerWorkflow } from "./types";
|
||||||
|
|
||||||
|
const nextDir = path.join(__dirname, "..", "server");
|
||||||
|
const configFile = path.join(__dirname, "..", "server", "public", "config.js");
|
||||||
|
const dev = process.env.NODE_ENV !== "production";
|
||||||
|
|
||||||
export class LlamaIndexServer {
|
export class LlamaIndexServer {
|
||||||
port: number;
|
port: number;
|
||||||
app: ReturnType<typeof next>;
|
app: ReturnType<typeof next>;
|
||||||
workflowFactory: () => Promise<ServerWorkflow> | ServerWorkflow;
|
workflowFactory: () => Promise<ServerWorkflow> | ServerWorkflow;
|
||||||
|
|
||||||
constructor({ workflow, ...nextAppOptions }: LlamaIndexServerOptions) {
|
constructor({ workflow, ...nextAppOptions }: LlamaIndexServerOptions) {
|
||||||
const nextDir = path.join(__dirname, "../server");
|
this.app = next({ dev, dir: nextDir, ...nextAppOptions });
|
||||||
const dev = process.env.NODE_ENV !== "production";
|
|
||||||
this.app = next({ ...nextAppOptions, dev, dir: nextDir });
|
|
||||||
this.port = nextAppOptions.port ?? 3000;
|
this.port = nextAppOptions.port ?? 3000;
|
||||||
this.workflowFactory = workflow;
|
this.workflowFactory = workflow;
|
||||||
|
|
||||||
|
this.modifyConfig(nextAppOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
private modifyConfig(
|
||||||
|
options: Pick<LlamaIndexServerOptions, "starterQuestions">,
|
||||||
|
) {
|
||||||
|
// content in javascript format
|
||||||
|
const content = `
|
||||||
|
window.LLAMAINDEX = {
|
||||||
|
CHAT_API: '/api/chat',
|
||||||
|
STARTER_QUESTIONS: ${JSON.stringify(options.starterQuestions ?? [])}
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
fs.writeFileSync(configFile, content);
|
||||||
}
|
}
|
||||||
|
|
||||||
async start() {
|
async start() {
|
||||||
|
|||||||
@@ -1,5 +1,9 @@
|
|||||||
import { Workflow, type AgentWorkflowContext } from "@llamaindex/workflow";
|
import type {
|
||||||
import type { AgentInputData, AgentWorkflow } from "@llamaindex/workflow/agent";
|
AgentInputData,
|
||||||
|
AgentWorkflow,
|
||||||
|
AgentWorkflowContext,
|
||||||
|
Workflow,
|
||||||
|
} from "llamaindex";
|
||||||
import type next from "next";
|
import type next from "next";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,8 +22,9 @@ export type WorkflowFactory = (
|
|||||||
requestBody?: any,
|
requestBody?: any,
|
||||||
) => Promise<ServerWorkflow> | ServerWorkflow;
|
) => Promise<ServerWorkflow> | ServerWorkflow;
|
||||||
|
|
||||||
export type NextAppOptions = Omit<Parameters<typeof next>[0], "dir">;
|
export type NextAppOptions = Parameters<typeof next>[0];
|
||||||
|
|
||||||
export type LlamaIndexServerOptions = NextAppOptions & {
|
export type LlamaIndexServerOptions = NextAppOptions & {
|
||||||
workflow: WorkflowFactory;
|
workflow: WorkflowFactory;
|
||||||
|
starterQuestions?: string[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,62 @@
|
|||||||
|
import type { StreamData } from "ai";
|
||||||
|
import { type ChatMessage, Settings } from "llamaindex";
|
||||||
|
|
||||||
|
const NEXT_QUESTION_PROMPT = `You're a helpful assistant! Your task is to suggest the next question that user might ask.
|
||||||
|
Here is the conversation history
|
||||||
|
---------------------
|
||||||
|
{conversation}
|
||||||
|
---------------------
|
||||||
|
Given the conversation history, please give me 3 questions that user might ask next!
|
||||||
|
Your answer should be wrapped in three sticks which follows the following format:
|
||||||
|
\`\`\`
|
||||||
|
<question 1>
|
||||||
|
<question 2>
|
||||||
|
<question 3>
|
||||||
|
\`\`\`
|
||||||
|
`;
|
||||||
|
|
||||||
|
export const sendSuggestedQuestionsEvent = async (
|
||||||
|
dataStream: StreamData,
|
||||||
|
chatHistory: ChatMessage[] = [],
|
||||||
|
) => {
|
||||||
|
const questions = await generateNextQuestions(chatHistory);
|
||||||
|
if (questions.length > 0) {
|
||||||
|
dataStream.appendMessageAnnotation({
|
||||||
|
type: "suggested_questions",
|
||||||
|
data: questions,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function generateNextQuestions(conversation: ChatMessage[]) {
|
||||||
|
const conversationText = conversation
|
||||||
|
.map((message) => `${message.role}: ${message.content}`)
|
||||||
|
.join("\n");
|
||||||
|
const message = NEXT_QUESTION_PROMPT.replace(
|
||||||
|
"{conversation}",
|
||||||
|
conversationText,
|
||||||
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await Settings.llm.complete({ prompt: message });
|
||||||
|
const questions = extractQuestions(response.text);
|
||||||
|
return questions;
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error when generating the next questions: ", error);
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function extractQuestions(text: string): string[] {
|
||||||
|
// Extract the text inside the triple backticks
|
||||||
|
const contentMatch = text.match(/```(.*?)```/s);
|
||||||
|
const content = contentMatch?.[1] ?? "";
|
||||||
|
|
||||||
|
// Split the content by newlines to get each question
|
||||||
|
const questions = content
|
||||||
|
.split("\n")
|
||||||
|
.map((question) => question.trim())
|
||||||
|
.filter((question) => question !== "");
|
||||||
|
|
||||||
|
return questions;
|
||||||
|
}
|
||||||
@@ -1,18 +1,19 @@
|
|||||||
import type { ChatResponseChunk } from "@llamaindex/core/llms";
|
import { LlamaIndexAdapter, StreamData, type JSONValue } from "ai";
|
||||||
import type { EngineResponse } from "@llamaindex/core/schema";
|
import type {
|
||||||
import {
|
AgentInputData,
|
||||||
StopEvent,
|
ChatResponseChunk,
|
||||||
Workflow,
|
EngineResponse,
|
||||||
type AgentWorkflowContext,
|
} from "llamaindex";
|
||||||
} from "@llamaindex/workflow";
|
|
||||||
import {
|
import {
|
||||||
AgentStream,
|
AgentStream,
|
||||||
AgentWorkflow,
|
AgentWorkflow,
|
||||||
type AgentInputData,
|
StopEvent,
|
||||||
} from "@llamaindex/workflow/agent";
|
Workflow,
|
||||||
import { LlamaIndexAdapter, StreamData, type JSONValue } from "ai";
|
type AgentWorkflowContext,
|
||||||
|
} from "llamaindex";
|
||||||
import { ReadableStream } from "stream/web";
|
import { ReadableStream } from "stream/web";
|
||||||
import type { ServerWorkflow } from "../types";
|
import type { ServerWorkflow } from "../types";
|
||||||
|
import { sendSuggestedQuestionsEvent } from "./suggestion";
|
||||||
|
|
||||||
export async function runWorkflow(
|
export async function runWorkflow(
|
||||||
workflow: ServerWorkflow,
|
workflow: ServerWorkflow,
|
||||||
@@ -47,11 +48,19 @@ async function runAgentWorkflow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.close();
|
controller.close();
|
||||||
dataStream.close();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return LlamaIndexAdapter.toDataStreamResponse(stream, { data: dataStream });
|
return LlamaIndexAdapter.toDataStreamResponse(stream, {
|
||||||
|
data: dataStream,
|
||||||
|
callbacks: {
|
||||||
|
onFinal: async (content: string) => {
|
||||||
|
const history = chatHistory.concat({ role: "assistant", content });
|
||||||
|
await sendSuggestedQuestionsEvent(dataStream, history);
|
||||||
|
dataStream.close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async function runCustomWorkflow(
|
async function runCustomWorkflow(
|
||||||
@@ -77,9 +86,20 @@ async function runCustomWorkflow(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.close();
|
controller.close();
|
||||||
dataStream.close();
|
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
return LlamaIndexAdapter.toDataStreamResponse(stream, { data: dataStream });
|
return LlamaIndexAdapter.toDataStreamResponse(stream, {
|
||||||
|
data: dataStream,
|
||||||
|
callbacks: {
|
||||||
|
onFinal: async (content: string) => {
|
||||||
|
const history = agentInput.chatHistory?.concat({
|
||||||
|
role: "assistant",
|
||||||
|
content,
|
||||||
|
});
|
||||||
|
await sendSuggestedQuestionsEvent(dataStream, history);
|
||||||
|
dataStream.close();
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
Generated
+39
-23
@@ -1767,17 +1767,38 @@ importers:
|
|||||||
packages/server:
|
packages/server:
|
||||||
dependencies:
|
dependencies:
|
||||||
'@llamaindex/chat-ui':
|
'@llamaindex/chat-ui':
|
||||||
specifier: 0.3.1
|
specifier: 0.3.2
|
||||||
version: 0.3.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
'@llamaindex/core':
|
'@radix-ui/react-accordion':
|
||||||
specifier: workspace:*
|
specifier: ^1.2.2
|
||||||
version: link:../core
|
version: 1.2.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
'@llamaindex/workflow':
|
'@radix-ui/react-collapsible':
|
||||||
specifier: workspace:*
|
specifier: ^1.0.3
|
||||||
version: link:../workflow
|
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-select':
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.6(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
|
'@radix-ui/react-slot':
|
||||||
|
specifier: ^1.0.2
|
||||||
|
version: 1.1.2(@types/react@19.0.10)(react@19.0.0)
|
||||||
|
'@radix-ui/react-tabs':
|
||||||
|
specifier: ^1.1.0
|
||||||
|
version: 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
ai:
|
ai:
|
||||||
specifier: ^4.2.0
|
specifier: ^4.2.0
|
||||||
version: 4.2.0(react@19.0.0)(zod@3.24.2)
|
version: 4.2.0(react@19.0.0)(zod@3.24.2)
|
||||||
|
class-variance-authority:
|
||||||
|
specifier: ^0.7.1
|
||||||
|
version: 0.7.1
|
||||||
|
clsx:
|
||||||
|
specifier: ^2.1.1
|
||||||
|
version: 2.1.1
|
||||||
|
llamaindex:
|
||||||
|
specifier: workspace:*
|
||||||
|
version: link:../llamaindex
|
||||||
|
lucide-react:
|
||||||
|
specifier: ^0.460.0
|
||||||
|
version: 0.460.0(react@19.0.0)
|
||||||
next:
|
next:
|
||||||
specifier: 15.2.3
|
specifier: 15.2.3
|
||||||
version: 15.2.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
version: 15.2.3(@opentelemetry/api@1.9.0)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
@@ -1787,6 +1808,9 @@ importers:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^19.0.0
|
specifier: ^19.0.0
|
||||||
version: 19.0.0(react@19.0.0)
|
version: 19.0.0(react@19.0.0)
|
||||||
|
tailwind-merge:
|
||||||
|
specifier: ^2.6.0
|
||||||
|
version: 2.6.0
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@eslint/eslintrc':
|
'@eslint/eslintrc':
|
||||||
specifier: ^3
|
specifier: ^3
|
||||||
@@ -2421,10 +2445,6 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
'@babel/core': ^7.0.0-0
|
'@babel/core': ^7.0.0-0
|
||||||
|
|
||||||
'@babel/runtime@7.26.7':
|
|
||||||
resolution: {integrity: sha512-AOPI3D+a8dXnja+iwsUqGRjr1BbZIe771sXdapOtYI531gSqpi92vXivKcq2asu/DFpdl1ceFAKZyRzK2PCVcQ==}
|
|
||||||
engines: {node: '>=6.9.0'}
|
|
||||||
|
|
||||||
'@babel/runtime@7.26.9':
|
'@babel/runtime@7.26.9':
|
||||||
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
|
resolution: {integrity: sha512-aA63XwOkcl4xxQa3HjPMqOP6LiK0ZDv3mUPYEFXkpHbaFjtGggE1A61FjFzJnB+p7/oy2gA8E+rcBNl/zC1tMg==}
|
||||||
engines: {node: '>=6.9.0'}
|
engines: {node: '>=6.9.0'}
|
||||||
@@ -3714,8 +3734,8 @@ packages:
|
|||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
|
||||||
'@llamaindex/chat-ui@0.3.1':
|
'@llamaindex/chat-ui@0.3.2':
|
||||||
resolution: {integrity: sha512-sF6axN9LviewAxvBbqkF3u3K0yvIt74prio7uiVruFVT/AYkRlIk721QXTPBscf+ZvyzAqjh0Nx0BoGiZUzBCw==}
|
resolution: {integrity: sha512-YcQOghcxutqHK9KO2CRSws0inDR5bbMZkmpUFJtC2aWcHjWi8wYbzVZjRVl1vrb3VCk+VInKOhFUTW9hEkzydA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
react: ^18.2.0 || ^19.0.0 || ^19.0.0-rc
|
||||||
|
|
||||||
@@ -13676,10 +13696,6 @@ snapshots:
|
|||||||
'@babel/core': 7.26.8
|
'@babel/core': 7.26.8
|
||||||
'@babel/helper-plugin-utils': 7.26.5
|
'@babel/helper-plugin-utils': 7.26.5
|
||||||
|
|
||||||
'@babel/runtime@7.26.7':
|
|
||||||
dependencies:
|
|
||||||
regenerator-runtime: 0.14.1
|
|
||||||
|
|
||||||
'@babel/runtime@7.26.9':
|
'@babel/runtime@7.26.9':
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime: 0.14.1
|
regenerator-runtime: 0.14.1
|
||||||
@@ -14934,7 +14950,7 @@ snapshots:
|
|||||||
- react-dom
|
- react-dom
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
'@llamaindex/chat-ui@0.3.1(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
'@llamaindex/chat-ui@0.3.2(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@llamaindex/pdf-viewer': 1.3.0(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
'@llamaindex/pdf-viewer': 1.3.0(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
'@radix-ui/react-collapsible': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
'@radix-ui/react-collapsible': 1.1.3(@types/react-dom@19.0.4(@types/react@19.0.10))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||||
@@ -14981,14 +14997,14 @@ snapshots:
|
|||||||
|
|
||||||
'@manypkg/find-root@1.1.0':
|
'@manypkg/find-root@1.1.0':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.7
|
'@babel/runtime': 7.26.9
|
||||||
'@types/node': 12.20.55
|
'@types/node': 12.20.55
|
||||||
find-up: 4.1.0
|
find-up: 4.1.0
|
||||||
fs-extra: 8.1.0
|
fs-extra: 8.1.0
|
||||||
|
|
||||||
'@manypkg/get-packages@1.1.3':
|
'@manypkg/get-packages@1.1.3':
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.26.7
|
'@babel/runtime': 7.26.9
|
||||||
'@changesets/types': 4.1.0
|
'@changesets/types': 4.1.0
|
||||||
'@manypkg/find-root': 1.1.0
|
'@manypkg/find-root': 1.1.0
|
||||||
fs-extra: 8.1.0
|
fs-extra: 8.1.0
|
||||||
@@ -19416,7 +19432,7 @@ snapshots:
|
|||||||
transitivePeerDependencies:
|
transitivePeerDependencies:
|
||||||
- supports-color
|
- supports-color
|
||||||
|
|
||||||
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)):
|
eslint-module-utils@2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2)):
|
||||||
dependencies:
|
dependencies:
|
||||||
debug: 3.2.7
|
debug: 3.2.7
|
||||||
optionalDependencies:
|
optionalDependencies:
|
||||||
@@ -19467,7 +19483,7 @@ snapshots:
|
|||||||
doctrine: 2.1.0
|
doctrine: 2.1.0
|
||||||
eslint: 9.22.0(jiti@2.4.2)
|
eslint: 9.22.0(jiti@2.4.2)
|
||||||
eslint-import-resolver-node: 0.3.9
|
eslint-import-resolver-node: 0.3.9
|
||||||
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0(eslint-plugin-import@2.31.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2)))(eslint@9.22.0(jiti@2.4.2))
|
eslint-module-utils: 2.12.0(@typescript-eslint/parser@8.24.0(eslint@9.22.0(jiti@2.4.2))(typescript@5.7.3))(eslint-import-resolver-node@0.3.9)(eslint-import-resolver-typescript@3.7.0)(eslint@9.22.0(jiti@2.4.2))
|
||||||
hasown: 2.0.2
|
hasown: 2.0.2
|
||||||
is-core-module: 2.16.1
|
is-core-module: 2.16.1
|
||||||
is-glob: 4.0.3
|
is-glob: 4.0.3
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
# @llamaindex/unit-test
|
# @llamaindex/unit-test
|
||||||
|
|
||||||
|
## 0.1.13
|
||||||
|
|
||||||
|
### Patch Changes
|
||||||
|
|
||||||
|
- Updated dependencies [75d6e29]
|
||||||
|
- llamaindex@0.9.13
|
||||||
|
|
||||||
## 0.1.12
|
## 0.1.12
|
||||||
|
|
||||||
### Patch Changes
|
### Patch Changes
|
||||||
|
|||||||
+1
-1
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "@llamaindex/unit-test",
|
"name": "@llamaindex/unit-test",
|
||||||
"private": true,
|
"private": true,
|
||||||
"version": "0.1.12",
|
"version": "0.1.13",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "vitest run"
|
"test": "vitest run"
|
||||||
|
|||||||
Reference in New Issue
Block a user