├── .gitignore ├── embed-documentation ├── requirements.txt ├── .env.example ├── main.sh └── main.py ├── app ├── requirements.txt ├── prompts │ └── rag.json └── app.py ├── .env.example ├── chainlit.md ├── public ├── terminal.svg ├── learn.svg ├── logo_dark.svg └── logo_light.svg ├── copilot └── index.html ├── README.md └── .chainlit ├── config.toml └── translations └── en-US.json /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | __pycache__ 3 | .env 4 | documentation 5 | 6 | -------------------------------------------------------------------------------- /embed-documentation/requirements.txt: -------------------------------------------------------------------------------- 1 | openai>=1.9.0 2 | pinecone-client>=3.0.2 3 | 4 | -------------------------------------------------------------------------------- /app/requirements.txt: -------------------------------------------------------------------------------- 1 | openai>=1.9.0 2 | pinecone-client>=3.0.2 3 | chainlit==1.1.300rc2 4 | discord>=2.3.0 -------------------------------------------------------------------------------- /embed-documentation/.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY= 2 | PINECONE_API_KEY= 3 | PINECONE_CLOUD= 4 | PINECONE_REGION= 5 | -------------------------------------------------------------------------------- /.env.example: -------------------------------------------------------------------------------- 1 | LITERAL_API_KEY= 2 | OPENAI_API_KEY= 3 | PINECONE_API_KEY= 4 | PINECONE_CLOUD= 5 | PINECONE_REGION= 6 | 7 | -------------------------------------------------------------------------------- /chainlit.md: -------------------------------------------------------------------------------- 1 | # Welcome to Literal! 2 | 3 | Hi there, Developer! 👋 We're excited to have you on board. Ask us about the documentation! -------------------------------------------------------------------------------- /public/terminal.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /copilot/index.html: -------------------------------------------------------------------------------- 1 | 2 |
3 | 4 | 5 | 6 | 19 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /public/learn.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /embed-documentation/main.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | SCRIPT=$(realpath "$0") 4 | SCRIPT_DIR=$(dirname "$SCRIPT") 5 | 6 | rm -rf ./cookbooks ./documentation 7 | mkdir -p ./cookbooks ./documentation 8 | 9 | git clone git@github.com:Chainlit/literal-docs.git 10 | git clone git@github.com:Chainlit/literal-cookbook.git 11 | 12 | # Use *.mdx from documentation and README.md, *.py and *.ts from cookbook 13 | find ./literal-docs -name "*.mdx" -exec bash -c 'newname="./documentation/$(echo {} | sed "s|/|_|g")"; cp "{}" "$newname"' \; 14 | find ./literal-cookbook \( -name "README.md" -o -name "*.py" -o -name "*.ts" \) -exec bash -c 'newname="./cookbooks/$(echo {} | sed "s|/|_|g")"; cp "{}" "$newname"' \; 15 | 16 | rm -rf ./literal-docs 17 | rm -rf ./literal-cookbook 18 | 19 | python3 "$SCRIPT_DIR/main.py" 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Documentation - RAG application 2 | 3 | The RAG chatbot application for the Literal platform [documentation](https://docs.getliteral.ai/introduction). 4 | 5 | ## How to use? 6 | 7 | ### Embed the documentation 8 | 9 | To upload the latest Literal documentation embeddings to Pinecone: 10 | 11 | - `cp embed-documentation/.env.example embed-documentation/.env` 12 | - Get an OpenAI API key [here](https://platform.openai.com/docs/quickstart/step-2-setup-your-api-key) 13 | - Find your Pinecone API key [here](https://docs.pinecone.io/docs/authentication#finding-your-pinecone-api-key) 14 | - `pip install -r embed-documentation/requirements.txt` 15 | - `./embed-documentation/main.sh` 16 | 17 | ### The Chainlit Application 18 | - `cp app/.env.example .env` 19 | - Obtain a Literal API key [here](https://docs.getliteral.ai/get-started/installation#how-to-get-my-api-key) 20 | - Get an OpenAI API key [here](https://platform.openai.com/docs/quickstart/step-2-setup-your-api-key) 21 | - Find your Pinecone API key [here](https://docs.pinecone.io/docs/authentication#finding-your-pinecone-api-key) 22 | - `pip install -r app/requirements.txt` 23 | - `chainlit run app/app.py` 24 | 25 | -------------------------------------------------------------------------------- /app/prompts/rag.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "RAG prompt - Tooled", 3 | "template_messages": [ 4 | { 5 | "role": "system", 6 | "content": "You are a helpful assistant who answers questions related to Literal. You have two tools at your disposal to retrieve context about the Literal documentation and Literal cookbooks. Keep it short, and if available prefer responding with code." 7 | } 8 | ], 9 | "tools": [ 10 | { 11 | "type": "function", 12 | "function": { 13 | "name": "get_relevant_documentation_chunks", 14 | "description": "Retrieves great content to answer questions monitoring an application with Literal AI and the concepts involved in features around observability and evaluation.", 15 | "parameters": { 16 | "type": "object", 17 | "properties": { 18 | "question": { 19 | "type": "string", 20 | "description": "The question or query to answer on the documentation." 21 | }, 22 | "top_k": { 23 | "type": "integer", 24 | "description": "The number of documentation chunks to retrieve, e.g. 5." 25 | } 26 | }, 27 | "required": ["question"] 28 | } 29 | } 30 | }, 31 | { 32 | "type": "function", 33 | "function": { 34 | "name": "get_relevant_cookbooks_chunks", 35 | "description": "Retrieves great content to answer questions about Literal APIs or capabilities and have real-world Python examples of how to use them.", 36 | "parameters": { 37 | "type": "object", 38 | "properties": { 39 | "question": { 40 | "type": "string", 41 | "description": "The question or query to answer on Literal use case." 42 | }, 43 | "top_k": { 44 | "type": "integer", 45 | "description": "The number of cookbooks chunks to retrieve, e.g. 5." 46 | } 47 | }, 48 | "required": ["question"] 49 | } 50 | } 51 | } 52 | ], 53 | "settings": { 54 | "provider": "openai", 55 | "stop": [], 56 | "model": "gpt-4o", 57 | "top_p": 1, 58 | "max_tokens": 512, 59 | "temperature": 0, 60 | "presence_penalty": 0, 61 | "frequency_penalty": 0 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /.chainlit/config.toml: -------------------------------------------------------------------------------- 1 | [project] 2 | # Whether to enable telemetry (default: true). No personal data is collected. 3 | enable_telemetry = true 4 | 5 | 6 | # List of environment variables to be provided by each user to use the app. 7 | user_env = [] 8 | 9 | # Duration (in seconds) during which the session is saved when the connection is lost 10 | session_timeout = 3600 11 | 12 | # Enable third parties caching (e.g LangChain cache) 13 | cache = false 14 | 15 | # Authorized origins 16 | allow_origins = ["*"] 17 | 18 | # Follow symlink for asset mount (see https://github.com/Chainlit/chainlit/issues/317) 19 | # follow_symlink = false 20 | 21 | [features] 22 | # Process and display HTML in messages. This can be a security risk (see https://stackoverflow.com/questions/19603097/why-is-it-dangerous-to-render-user-generated-html-or-javascript) 23 | unsafe_allow_html = false 24 | 25 | # Process and display mathematical expressions. This can clash with "$" characters in messages. 26 | latex = false 27 | 28 | # Automatically tag threads with the current chat profile (if a chat profile is used) 29 | auto_tag_thread = true 30 | 31 | # Authorize users to spontaneously upload files with messages 32 | [features.spontaneous_file_upload] 33 | enabled = true 34 | accept = ["*/*"] 35 | max_files = 20 36 | max_size_mb = 500 37 | 38 | [features.audio] 39 | # Threshold for audio recording 40 | min_decibels = -45 41 | # Delay for the user to start speaking in MS 42 | initial_silence_timeout = 3000 43 | # Delay for the user to continue speaking in MS. If the user stops speaking for this duration, the recording will stop. 44 | silence_timeout = 1500 45 | # Above this duration (MS), the recording will forcefully stop. 46 | max_duration = 15000 47 | # Duration of the audio chunks in MS 48 | chunk_duration = 1000 49 | # Sample rate of the audio 50 | sample_rate = 44100 51 | 52 | [UI] 53 | # Name of the assistant. 54 | name = "Assistant" 55 | 56 | # Description of the assistant. This is used for HTML tags. 57 | # description = "" 58 | 59 | # Large size content are by default collapsed for a cleaner ui 60 | default_collapse_content = true 61 | 62 | # Hide the chain of thought details from the user in the UI. 63 | hide_cot = false 64 | 65 | # Link to your github repo. This will add a github button in the UI's header. 66 | # github = "" 67 | 68 | # Specify a CSS file that can be used to customize the user interface. 69 | # The CSS file can be served from the public directory or via an external link. 70 | # custom_css = "/public/test.css" 71 | 72 | # Specify a Javascript file that can be used to customize the user interface. 73 | # The Javascript file can be served from the public directory. 74 | # custom_js = "/public/test.js" 75 | 76 | # Specify a custom font url. 77 | # custom_font = "https://fonts.googleapis.com/css2?family=Inter:wght@400;500;700&display=swap" 78 | 79 | # Specify a custom meta image url. 80 | # custom_meta_image_url = "https://chainlit-cloud.s3.eu-west-3.amazonaws.com/logo/chainlit_banner.png" 81 | 82 | # Specify a custom build directory for the frontend. 83 | # This can be used to customize the frontend code. 84 | # Be careful: If this is a relative path, it should not start with a slash. 85 | # custom_build = "./public/build" 86 | 87 | [UI.theme] 88 | default = "light" 89 | #layout = "wide" 90 | #font_family = "Inter, sans-serif" 91 | # Override default MUI light theme. (Check theme.ts) 92 | [UI.theme.light] 93 | #background = "#FAFAFA" 94 | #paper = "#FFFFFF" 95 | 96 | [UI.theme.light.primary] 97 | #main = "#F80061" 98 | #dark = "#980039" 99 | #light = "#FFE7EB" 100 | [UI.theme.light.text] 101 | #primary = "#212121" 102 | #secondary = "#616161" 103 | 104 | # Override default MUI dark theme. (Check theme.ts) 105 | [UI.theme.dark] 106 | #background = "#FAFAFA" 107 | #paper = "#FFFFFF" 108 | 109 | [UI.theme.dark.primary] 110 | #main = "#F80061" 111 | #dark = "#980039" 112 | #light = "#FFE7EB" 113 | [UI.theme.dark.text] 114 | #primary = "#EEEEEE" 115 | #secondary = "#BDBDBD" 116 | 117 | [meta] 118 | generated_by = "1.1.300rc0" 119 | -------------------------------------------------------------------------------- /embed-documentation/main.py: -------------------------------------------------------------------------------- 1 | import os 2 | import re 3 | from dotenv import load_dotenv 4 | from openai import OpenAI 5 | from pinecone import Pinecone, ServerlessSpec 6 | 7 | load_dotenv() 8 | 9 | documentation_path = os.path.join(os.getcwd(), "./documentation") 10 | cookbooks_path = os.path.join(os.getcwd(), "./cookbooks") 11 | 12 | openai_client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY")) 13 | 14 | pinecone_client = Pinecone(api_key=os.environ.get("PINECONE_API_KEY")) 15 | pinecone_spec = ServerlessSpec( 16 | cloud=os.environ.get("PINECONE_CLOUD"), region=os.environ.get("PINECONE_REGION") 17 | ) 18 | 19 | 20 | def create_dataset(id, path): 21 | values = [] 22 | 23 | for filename in os.listdir(path): 24 | with open(os.path.join(path, filename), "r") as file: 25 | file_content = file.read() 26 | 27 | if filename.endswith("__init__.py"): 28 | continue 29 | 30 | if not filename.endswith("mdx") and not filename.endswith("md"): 31 | values.append({"title": filename, "description": "Cookbook", "content": file_content}) 32 | 33 | # Extract file metadata 34 | metadata = re.search( 35 | r"---\ntitle: (.+?)(?:\ndescription: (.+?))?\n---", file_content, re.DOTALL 36 | ) 37 | 38 | # Skip if missing no metadata 39 | if not metadata: 40 | continue 41 | file_title, file_description = metadata.groups() 42 | 43 | # Strip newlines except for code blocks 44 | content = file_content.split("---", 2)[-1] 45 | content_parts = re.split(r"(```.*?```)", content, flags=re.DOTALL) 46 | 47 | for i in range(len(content_parts)): 48 | if not content_parts[i].startswith("```"): 49 | content_parts[i] = content_parts[i].replace("\n", " ") 50 | content = "".join(content_parts) 51 | 52 | values.append({"title": file_title, "description": file_description, "content": content}) 53 | 54 | return {"id": id, "values": values} 55 | 56 | 57 | def create_embedding_set(dataset, model="text-embedding-ada-002"): 58 | values = [] 59 | 60 | for item in dataset["values"]: 61 | input = f"title:{item['title']}_description:{item['description']}_content:{item['content']}" 62 | response = ( 63 | openai_client.embeddings.create(input=input, model=model).data[0].embedding 64 | ) 65 | values.append({"text": input, "values": response}) 66 | 67 | return {"id": dataset["id"], "values": values} 68 | 69 | def create_pinecone_index(name, client, spec): 70 | if name in client.list_indexes().names(): 71 | client.delete_index(name) 72 | 73 | client.create_index(name, dimension=1536, metric="cosine", spec=spec) 74 | return pinecone_client.Index(name) 75 | 76 | def upload_to_index(index, embedding_set, batch_size=100): 77 | values = embedding_set["values"] 78 | total_values = len(values) 79 | 80 | for i in range(0, total_values, batch_size): 81 | batch = [] 82 | 83 | for j in range(i, min(i + batch_size, total_values)): 84 | batch.append( 85 | { 86 | "id": f"vector_{j}", 87 | "values": values[j]["values"], 88 | "metadata": { 89 | "dataset_id": embedding_set["id"], 90 | "text": values[j]["text"], 91 | }, 92 | } 93 | ) 94 | index.upsert(batch) 95 | 96 | 97 | dataset_documentation = create_dataset("dataset_documentation", documentation_path) 98 | dataset_cookbooks = create_dataset("dataset_cookbooks", cookbooks_path) 99 | print("Datasets created from documentation & cookbooks") 100 | 101 | embeddings_documentation = create_embedding_set(dataset_documentation) 102 | embeddings_cookbooks = create_embedding_set(dataset_cookbooks) 103 | print("Embeddings created from datasets") 104 | 105 | pinecone_index = create_pinecone_index( 106 | "literal-rag-index", pinecone_client, pinecone_spec 107 | ) 108 | print("Pinecone index created") 109 | 110 | upload_to_index(pinecone_index, embeddings_documentation) 111 | upload_to_index(pinecone_index, embeddings_cookbooks) 112 | print("Embeddings uploaded to index") 113 | -------------------------------------------------------------------------------- /public/logo_dark.svg: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /public/logo_light.svg: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /app/app.py: -------------------------------------------------------------------------------- 1 | import os 2 | import json 3 | import asyncio 4 | import discord 5 | import chainlit as cl 6 | from chainlit.discord.app import client as discord_client 7 | 8 | from dotenv import load_dotenv 9 | from openai import AsyncOpenAI 10 | from pinecone import Pinecone, ServerlessSpec # type: ignore 11 | 12 | from literalai import LiteralClient 13 | 14 | load_dotenv() 15 | 16 | client = LiteralClient() 17 | openai_client = AsyncOpenAI(api_key=os.environ.get("OPENAI_API_KEY")) 18 | 19 | pinecone_client = Pinecone(api_key=os.environ.get("PINECONE_API_KEY")) 20 | pinecone_spec = ServerlessSpec( 21 | cloud=os.environ.get("PINECONE_CLOUD"), 22 | region=os.environ.get("PINECONE_REGION"), 23 | ) 24 | 25 | cl.instrument_openai() 26 | 27 | prompt_path = os.path.join(os.getcwd(), "app/prompts/rag.json") 28 | 29 | # We load the RAG prompt in Literal to track prompt iteration and 30 | # enable LLM replays from Literal AI. 31 | with open(prompt_path, "r") as f: 32 | rag_prompt = json.load(f) 33 | 34 | prompt = client.api.get_or_create_prompt( 35 | name=rag_prompt["name"], 36 | template_messages=rag_prompt["template_messages"], 37 | settings=rag_prompt["settings"], 38 | tools=rag_prompt["tools"], 39 | ) 40 | 41 | 42 | def create_pinecone_index(name, client, spec): 43 | """ 44 | Create a Pinecone index if it does not already exist. 45 | """ 46 | if name not in client.list_indexes().names(): 47 | client.create_index(name, dimension=1536, metric="cosine", spec=spec) 48 | return pinecone_client.Index(name) 49 | 50 | 51 | pinecone_index = create_pinecone_index( 52 | "literal-rag-index", pinecone_client, pinecone_spec 53 | ) 54 | 55 | 56 | @cl.set_starters 57 | async def set_starters(): 58 | return [ 59 | cl.Starter( 60 | label="What is Literal AI?", 61 | message="What is Literal AI and what makes it unique?", 62 | icon="/public/learn.svg", 63 | ), 64 | cl.Starter( 65 | label="Install Literal AI SDKs", 66 | message="How to instrument my application with the Literal AI SDKs?", 67 | icon="/public/terminal.svg", 68 | ), 69 | ] 70 | 71 | 72 | @cl.step(name="Embed", type="embedding") 73 | async def embed(question, model="text-embedding-ada-002"): 74 | """ 75 | Embed a question using the specified model and return the embedding. 76 | """ 77 | embedding = await openai_client.embeddings.create(input=question, model=model) 78 | 79 | return embedding.data[0].embedding 80 | 81 | 82 | @cl.step(name="Retrieve", type="retrieval") 83 | async def retrieve(embedding, dataset_id, top_k): 84 | """ 85 | Retrieve top_k closest vectors from the Pinecone index using the provided embedding. 86 | """ 87 | if pinecone_index == None: 88 | raise Exception("Pinecone index not initialized") 89 | response = pinecone_index.query( 90 | vector=embedding, top_k=top_k, include_metadata=True, 91 | filter={ 92 | "dataset_id": {"$eq": dataset_id} 93 | } 94 | ) 95 | return response.to_dict() 96 | 97 | 98 | async def get_relevant_chunks(question, dataset_id, top_k=5): 99 | """ 100 | Retrieve relevant chunks from dataset based on the question embedding. 101 | """ 102 | embedding = await embed(question) 103 | 104 | retrieved_chunks = await retrieve(embedding, dataset_id, top_k) 105 | 106 | return [match["metadata"]["text"] for match in retrieved_chunks["matches"]] 107 | 108 | 109 | @cl.step(name="Documentation Retrieval", type="tool") 110 | async def get_relevant_documentation_chunks(question, top_k=5): 111 | return await get_relevant_chunks(question, "dataset_documentation", top_k) 112 | 113 | 114 | @cl.step(name="Cookbooks Retrieval", type="tool") 115 | async def get_relevant_cookbooks_chunks(question, top_k=5): 116 | return await get_relevant_chunks(question, "dataset_cookbooks", top_k) 117 | 118 | 119 | async def llm_tool(question): 120 | """ 121 | Generate a response from the LLM based on the user's question. 122 | """ 123 | messages = cl.user_session.get("messages", []) or [] 124 | messages.append({"role": "user", "content": question}) 125 | 126 | settings = cl.user_session.get("settings", {}) or {} 127 | 128 | response = await openai_client.chat.completions.create( 129 | messages=messages, 130 | **settings, 131 | tools=cl.user_session.get("tools"), 132 | tool_choice="auto" 133 | ) 134 | 135 | response_message = response.choices[0].message 136 | messages.append(response_message) 137 | return response_message 138 | 139 | 140 | async def run_multiple(tool_calls): 141 | """ 142 | Execute multiple tool calls asynchronously. 143 | """ 144 | available_tools = { 145 | "get_relevant_documentation_chunks": get_relevant_documentation_chunks, 146 | "get_relevant_cookbooks_chunks": get_relevant_cookbooks_chunks 147 | } 148 | 149 | async def run_single(tool_call): 150 | function_name = tool_call.function.name 151 | function_to_call = available_tools[function_name] 152 | function_args = json.loads(tool_call.function.arguments) 153 | 154 | function_response = await function_to_call( 155 | question=function_args.get("question"), 156 | top_k=function_args.get("top_k", 5), 157 | ) 158 | return { 159 | "tool_call_id": tool_call.id, 160 | "role": "tool", 161 | "name": function_name, 162 | "content": "\n".join(function_response), 163 | } 164 | 165 | # Run tool calls in parallel. 166 | tool_results = await asyncio.gather( 167 | *(run_single(tool_call) for tool_call in tool_calls) 168 | ) 169 | return tool_results 170 | 171 | 172 | async def llm_answer(tool_results): 173 | """ 174 | Generate an answer from the LLM based on the results of tool calls. 175 | """ 176 | messages = cl.user_session.get("messages", []) or [] 177 | messages.extend(tool_results) 178 | 179 | settings = cl.user_session.get("settings", {}) or {} 180 | 181 | stream = await openai_client.chat.completions.create( 182 | messages=messages, 183 | **settings, 184 | stream=True 185 | ) 186 | 187 | answer_message: cl.Message = cl.user_session.get("answer_message") 188 | async for part in stream: 189 | if token := part.choices[0].delta.content or "": 190 | await answer_message.stream_token(token) 191 | 192 | await answer_message.send() 193 | messages.append({"role": "assistant", "content": answer_message.content}) 194 | return answer_message 195 | 196 | 197 | @cl.step(name="RAG Agent", type="run") 198 | async def rag_agent(question): 199 | """ 200 | Coordinate the RAG agent flow to generate a response based on the user's question. 201 | """ 202 | # Step 1 - Call LLM with tool: plan to use tool or give message. 203 | message = await llm_tool(question) 204 | 205 | # Potentially several calls to retrieve context. 206 | if not message.tool_calls: 207 | answer_message: cl.Message = cl.user_session.get("answer_message") 208 | answer_message.content = message.content 209 | await answer_message.send() 210 | return message.content 211 | 212 | # Step 2 - Run the tool calls. 213 | tool_results = await run_multiple(message.tool_calls) 214 | 215 | # Step 3 - Call LLM to answer based on contexts (streamed). 216 | return (await llm_answer(tool_results)).content 217 | 218 | 219 | @cl.on_chat_start 220 | async def on_chat_start(): 221 | """ 222 | Send a welcome message and set up the initial user session on chat start. 223 | """ 224 | 225 | client_type = cl.user_session.get("client_type") 226 | 227 | cl.user_session.set("messages", prompt.format_messages()) 228 | cl.user_session.set("settings", prompt.settings) 229 | cl.user_session.set("tools", prompt.tools) 230 | 231 | if client_type == "discord": 232 | # Discord limits the number of characters to 2000 233 | prompt.settings["max_tokens"] = 400 234 | 235 | 236 | async def use_discord_history(limit = 10): 237 | messages = cl.user_session.get("messages", []) 238 | channel: discord.abc.MessageableChannel = cl.user_session.get("discord_channel") 239 | 240 | if channel: 241 | cl.user_session.get("messages") 242 | discord_messages = [message async for message in channel.history(limit=limit)] 243 | 244 | # Go through last `limit` messages and remove the current message. 245 | for x in discord_messages[::-1][:-1]: 246 | messages.append({ 247 | "role": "assistant" if x.author.name == discord_client.user.name else "user", 248 | "content": x.clean_content if x.clean_content else x.channel.name # first message is empty 249 | }) 250 | 251 | 252 | @cl.on_message 253 | async def main(message: cl.Message): 254 | """ 255 | Main message handler for incoming user messages. 256 | """ 257 | # The user session resets on every Discord message. Add previous chat messages manually. 258 | await use_discord_history() 259 | 260 | answer_message = cl.Message(content="") 261 | cl.user_session.set("answer_message", answer_message) 262 | await rag_agent(message.content) 263 | -------------------------------------------------------------------------------- /.chainlit/translations/en-US.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "atoms": { 4 | "buttons": { 5 | "userButton": { 6 | "menu": { 7 | "settings": "Settings", 8 | "settingsKey": "S", 9 | "APIKeys": "API Keys", 10 | "logout": "Logout" 11 | } 12 | } 13 | } 14 | }, 15 | "molecules": { 16 | "newChatButton": { 17 | "newChat": "New Chat" 18 | }, 19 | "tasklist": { 20 | "TaskList": { 21 | "title": "\ud83d\uddd2\ufe0f Task List", 22 | "loading": "Loading...", 23 | "error": "An error occured" 24 | } 25 | }, 26 | "attachments": { 27 | "cancelUpload": "Cancel upload", 28 | "removeAttachment": "Remove attachment" 29 | }, 30 | "newChatDialog": { 31 | "createNewChat": "Create new chat?", 32 | "clearChat": "This will clear the current messages and start a new chat.", 33 | "cancel": "Cancel", 34 | "confirm": "Confirm" 35 | }, 36 | "settingsModal": { 37 | "settings": "Settings", 38 | "expandMessages": "Expand Messages", 39 | "hideChainOfThought": "Hide Chain of Thought", 40 | "darkMode": "Dark Mode" 41 | }, 42 | "detailsButton": { 43 | "using": "Using", 44 | "used": "Used" 45 | }, 46 | "auth": { 47 | "authLogin": { 48 | "title": "Login to access the app.", 49 | "form": { 50 | "email": "Email address", 51 | "password": "Password", 52 | "noAccount": "Don't have an account?", 53 | "alreadyHaveAccount": "Already have an account?", 54 | "signup": "Sign Up", 55 | "signin": "Sign In", 56 | "or": "OR", 57 | "continue": "Continue", 58 | "forgotPassword": "Forgot password?", 59 | "passwordMustContain": "Your password must contain:", 60 | "emailRequired": "email is a required field", 61 | "passwordRequired": "password is a required field" 62 | }, 63 | "error": { 64 | "default": "Unable to sign in.", 65 | "signin": "Try signing in with a different account.", 66 | "oauthsignin": "Try signing in with a different account.", 67 | "redirect_uri_mismatch": "The redirect URI is not matching the oauth app configuration.", 68 | "oauthcallbackerror": "Try signing in with a different account.", 69 | "oauthcreateaccount": "Try signing in with a different account.", 70 | "emailcreateaccount": "Try signing in with a different account.", 71 | "callback": "Try signing in with a different account.", 72 | "oauthaccountnotlinked": "To confirm your identity, sign in with the same account you used originally.", 73 | "emailsignin": "The e-mail could not be sent.", 74 | "emailverify": "Please verify your email, a new email has been sent.", 75 | "credentialssignin": "Sign in failed. Check the details you provided are correct.", 76 | "sessionrequired": "Please sign in to access this page." 77 | } 78 | }, 79 | "authVerifyEmail": { 80 | "almostThere": "You're almost there! We've sent an email to ", 81 | "verifyEmailLink": "Please click on the link in that email to complete your signup.", 82 | "didNotReceive": "Can't find the email?", 83 | "resendEmail": "Resend email", 84 | "goBack": "Go Back", 85 | "emailSent": "Email sent successfully.", 86 | "verifyEmail": "Verify your email address" 87 | }, 88 | "providerButton": { 89 | "continue": "Continue with {{provider}}", 90 | "signup": "Sign up with {{provider}}" 91 | }, 92 | "authResetPassword": { 93 | "newPasswordRequired": "New password is a required field", 94 | "passwordsMustMatch": "Passwords must match", 95 | "confirmPasswordRequired": "Confirm password is a required field", 96 | "newPassword": "New password", 97 | "confirmPassword": "Confirm password", 98 | "resetPassword": "Reset Password" 99 | }, 100 | "authForgotPassword": { 101 | "email": "Email address", 102 | "emailRequired": "email is a required field", 103 | "emailSent": "Please check the email address {{email}} for instructions to reset your password.", 104 | "enterEmail": "Enter your email address and we will send you instructions to reset your password.", 105 | "resendEmail": "Resend email", 106 | "continue": "Continue", 107 | "goBack": "Go Back" 108 | } 109 | } 110 | }, 111 | "organisms": { 112 | "chat": { 113 | "history": { 114 | "index": { 115 | "showHistory": "Show history", 116 | "lastInputs": "Last Inputs", 117 | "noInputs": "Such empty...", 118 | "loading": "Loading..." 119 | } 120 | }, 121 | "inputBox": { 122 | "input": { 123 | "placeholder": "Type your message here..." 124 | }, 125 | "speechButton": { 126 | "start": "Start recording", 127 | "stop": "Stop recording" 128 | }, 129 | "SubmitButton": { 130 | "sendMessage": "Send message", 131 | "stopTask": "Stop Task" 132 | }, 133 | "UploadButton": { 134 | "attachFiles": "Attach files" 135 | }, 136 | "waterMark": { 137 | "text": "Built with" 138 | } 139 | }, 140 | "Messages": { 141 | "index": { 142 | "running": "Running", 143 | "executedSuccessfully": "executed successfully", 144 | "failed": "failed", 145 | "feedbackUpdated": "Feedback updated", 146 | "updating": "Updating" 147 | } 148 | }, 149 | "dropScreen": { 150 | "dropYourFilesHere": "Drop your files here" 151 | }, 152 | "index": { 153 | "failedToUpload": "Failed to upload", 154 | "cancelledUploadOf": "Cancelled upload of", 155 | "couldNotReachServer": "Could not reach the server", 156 | "continuingChat": "Continuing previous chat" 157 | }, 158 | "settings": { 159 | "settingsPanel": "Settings panel", 160 | "reset": "Reset", 161 | "cancel": "Cancel", 162 | "confirm": "Confirm" 163 | } 164 | }, 165 | "threadHistory": { 166 | "sidebar": { 167 | "filters": { 168 | "FeedbackSelect": { 169 | "feedbackAll": "Feedback: All", 170 | "feedbackPositive": "Feedback: Positive", 171 | "feedbackNegative": "Feedback: Negative" 172 | }, 173 | "SearchBar": { 174 | "search": "Search" 175 | } 176 | }, 177 | "DeleteThreadButton": { 178 | "confirmMessage": "This will delete the thread as well as it's messages and elements.", 179 | "cancel": "Cancel", 180 | "confirm": "Confirm", 181 | "deletingChat": "Deleting chat", 182 | "chatDeleted": "Chat deleted" 183 | }, 184 | "index": { 185 | "pastChats": "Past Chats" 186 | }, 187 | "ThreadList": { 188 | "empty": "Empty...", 189 | "today": "Today", 190 | "yesterday": "Yesterday", 191 | "previous7days": "Previous 7 days", 192 | "previous30days": "Previous 30 days" 193 | }, 194 | "TriggerButton": { 195 | "closeSidebar": "Close sidebar", 196 | "openSidebar": "Open sidebar" 197 | } 198 | }, 199 | "Thread": { 200 | "backToChat": "Go back to chat", 201 | "chatCreatedOn": "This chat was created on" 202 | } 203 | }, 204 | "header": { 205 | "chat": "Chat", 206 | "readme": "Readme" 207 | } 208 | } 209 | }, 210 | "hooks": { 211 | "useLLMProviders": { 212 | "failedToFetchProviders": "Failed to fetch providers:" 213 | } 214 | }, 215 | "pages": { 216 | "Design": {}, 217 | "Env": { 218 | "savedSuccessfully": "Saved successfully", 219 | "requiredApiKeys": "Required API Keys", 220 | "requiredApiKeysInfo": "To use this app, the following API keys are required. The keys are stored on your device's local storage." 221 | }, 222 | "Page": { 223 | "notPartOfProject": "You are not part of this project." 224 | }, 225 | "ResumeButton": { 226 | "resumeChat": "Resume Chat" 227 | } 228 | } 229 | } --------------------------------------------------------------------------------