mirror of
https://github.com/langchain-ai/open-canvas.git
synced 2026-07-01 19:55:05 -04:00
show thiniing
This commit is contained in:
Vendored
+1
@@ -1,5 +1,6 @@
|
||||
{
|
||||
"cSpell.words": [
|
||||
"groq",
|
||||
"langchain",
|
||||
"langsmith",
|
||||
"opencanvas",
|
||||
|
||||
@@ -39,12 +39,14 @@
|
||||
"@langchain/community": "^0.3.28",
|
||||
"@langchain/core": "^0.3.36",
|
||||
"@langchain/google-genai": "^0.1.7",
|
||||
"@langchain/groq": "^0.1.3",
|
||||
"@langchain/langgraph": "^0.2.41",
|
||||
"@langchain/langgraph-sdk": "^0.0.36",
|
||||
"@langchain/ollama": "^0.1.4",
|
||||
"@langchain/openai": "^0.4.2",
|
||||
"@mendable/firecrawl-js": "1.10.1",
|
||||
"@nextjournal/lang-clojure": "^1.0.0",
|
||||
"@radix-ui/react-accordion": "^1.2.2",
|
||||
"@radix-ui/react-avatar": "^1.1.1",
|
||||
"@radix-ui/react-checkbox": "^1.1.2",
|
||||
"@radix-ui/react-dialog": "^1.1.2",
|
||||
|
||||
@@ -48,7 +48,7 @@ export const reflectNode = async (
|
||||
// This lets us "debounce" repeated requests to the memory graph
|
||||
// if the user is actively engaging in a conversation. This saves us $$ and
|
||||
// can help reduce the occurrence of duplicate memories.
|
||||
afterSeconds: 15,
|
||||
afterSeconds: 5 * 60, // 5 minutes
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import {
|
||||
OpenCanvasGraphAnnotation,
|
||||
OpenCanvasGraphReturnType,
|
||||
@@ -8,16 +9,23 @@ import { buildPrompt, createNewArtifactContent, validateState } from "./utils";
|
||||
import {
|
||||
createContextDocumentMessages,
|
||||
getFormattedReflections,
|
||||
getModelConfig,
|
||||
getModelFromConfig,
|
||||
isUsingO1MiniModel,
|
||||
optionallyGetSystemPromptFromConfig,
|
||||
} from "@/agent/utils";
|
||||
import { isArtifactMarkdownContent } from "@/lib/artifact_content_types";
|
||||
import { AIMessage } from "@langchain/core/messages";
|
||||
import {
|
||||
extractThinkingAndResponseTokens,
|
||||
isThinkingModel,
|
||||
} from "@/contexts/utils";
|
||||
|
||||
export const rewriteArtifact = async (
|
||||
state: typeof OpenCanvasGraphAnnotation.State,
|
||||
config: LangGraphRunnableConfig
|
||||
): Promise<OpenCanvasGraphReturnType> => {
|
||||
const { modelName } = getModelConfig(config);
|
||||
const smallModelWithConfig = (await getModelFromConfig(config)).withConfig({
|
||||
runName: "rewrite_artifact_model_call",
|
||||
});
|
||||
@@ -55,12 +63,25 @@ export const rewriteArtifact = async (
|
||||
recentHumanMessage,
|
||||
]);
|
||||
|
||||
let thinkingMessage: AIMessage | undefined;
|
||||
let artifactContentText = newArtifactResponse.content as string;
|
||||
|
||||
if (isThinkingModel(modelName)) {
|
||||
const { thinking, response } =
|
||||
extractThinkingAndResponseTokens(artifactContentText);
|
||||
thinkingMessage = new AIMessage({
|
||||
id: `thinking-${uuidv4()}`,
|
||||
content: thinking,
|
||||
});
|
||||
artifactContentText = response;
|
||||
}
|
||||
|
||||
const newArtifactContent = createNewArtifactContent({
|
||||
artifactType,
|
||||
state,
|
||||
currentArtifactContent,
|
||||
artifactMetaToolCall,
|
||||
newContent: newArtifactResponse.content as string,
|
||||
newContent: artifactContentText as string,
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -69,5 +90,7 @@ export const rewriteArtifact = async (
|
||||
currentIndex: state.artifact.contents.length + 1,
|
||||
contents: [...state.artifact.contents, newArtifactContent],
|
||||
},
|
||||
messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
_messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,10 +1,16 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { LangGraphRunnableConfig } from "@langchain/langgraph";
|
||||
import { getArtifactContent } from "../../../contexts/utils";
|
||||
import {
|
||||
extractThinkingAndResponseTokens,
|
||||
getArtifactContent,
|
||||
isThinkingModel,
|
||||
} from "../../../contexts/utils";
|
||||
import { isArtifactMarkdownContent } from "../../../lib/artifact_content_types";
|
||||
import { ArtifactV3, Reflections } from "../../../types";
|
||||
import {
|
||||
ensureStoreInConfig,
|
||||
formatReflections,
|
||||
getModelConfig,
|
||||
getModelFromConfig,
|
||||
} from "../../utils";
|
||||
import {
|
||||
@@ -15,11 +21,13 @@ import {
|
||||
CHANGE_ARTIFACT_TO_PIRATE_PROMPT,
|
||||
} from "../prompts";
|
||||
import { OpenCanvasGraphAnnotation, OpenCanvasGraphReturnType } from "../state";
|
||||
import { AIMessage } from "@langchain/core/messages";
|
||||
|
||||
export const rewriteArtifactTheme = async (
|
||||
state: typeof OpenCanvasGraphAnnotation.State,
|
||||
config: LangGraphRunnableConfig
|
||||
): Promise<OpenCanvasGraphReturnType> => {
|
||||
const { modelName } = getModelConfig(config);
|
||||
const smallModel = await getModelFromConfig(config);
|
||||
|
||||
const store = ensureStoreInConfig(config);
|
||||
@@ -110,6 +118,19 @@ export const rewriteArtifactTheme = async (
|
||||
{ role: "user", content: formattedPrompt },
|
||||
]);
|
||||
|
||||
let thinkingMessage: AIMessage | undefined;
|
||||
let artifactContentText = newArtifactValues.content as string;
|
||||
|
||||
if (isThinkingModel(modelName)) {
|
||||
const { thinking, response } =
|
||||
extractThinkingAndResponseTokens(artifactContentText);
|
||||
thinkingMessage = new AIMessage({
|
||||
id: `thinking-${uuidv4()}`,
|
||||
content: thinking,
|
||||
});
|
||||
artifactContentText = response;
|
||||
}
|
||||
|
||||
const newArtifact: ArtifactV3 = {
|
||||
...state.artifact,
|
||||
currentIndex: state.artifact.contents.length + 1,
|
||||
@@ -118,12 +139,14 @@ export const rewriteArtifactTheme = async (
|
||||
{
|
||||
...currentArtifactContent,
|
||||
index: state.artifact.contents.length + 1,
|
||||
fullMarkdown: newArtifactValues.content as string,
|
||||
fullMarkdown: artifactContentText,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
return {
|
||||
artifact: newArtifact,
|
||||
messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
_messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -1,8 +1,13 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { LangGraphRunnableConfig } from "@langchain/langgraph";
|
||||
import { getArtifactContent } from "../../../contexts/utils";
|
||||
import {
|
||||
extractThinkingAndResponseTokens,
|
||||
getArtifactContent,
|
||||
isThinkingModel,
|
||||
} from "../../../contexts/utils";
|
||||
import { isArtifactCodeContent } from "../../../lib/artifact_content_types";
|
||||
import { ArtifactCodeV3, ArtifactV3 } from "../../../types";
|
||||
import { getModelFromConfig } from "../../utils";
|
||||
import { getModelConfig, getModelFromConfig } from "../../utils";
|
||||
import {
|
||||
ADD_COMMENTS_TO_CODE_ARTIFACT_PROMPT,
|
||||
ADD_LOGS_TO_CODE_ARTIFACT_PROMPT,
|
||||
@@ -10,11 +15,13 @@ import {
|
||||
PORT_LANGUAGE_CODE_ARTIFACT_PROMPT,
|
||||
} from "../prompts";
|
||||
import { OpenCanvasGraphAnnotation, OpenCanvasGraphReturnType } from "../state";
|
||||
import { AIMessage } from "@langchain/core/messages";
|
||||
|
||||
export const rewriteCodeArtifactTheme = async (
|
||||
state: typeof OpenCanvasGraphAnnotation.State,
|
||||
config: LangGraphRunnableConfig
|
||||
): Promise<OpenCanvasGraphReturnType> => {
|
||||
const { modelName } = getModelConfig(config);
|
||||
const smallModel = await getModelFromConfig(config);
|
||||
|
||||
const currentArtifactContent = state.artifact
|
||||
@@ -80,13 +87,26 @@ export const rewriteCodeArtifactTheme = async (
|
||||
{ role: "user", content: formattedPrompt },
|
||||
]);
|
||||
|
||||
let thinkingMessage: AIMessage | undefined;
|
||||
let artifactContentText = newArtifactValues.content as string;
|
||||
|
||||
if (isThinkingModel(modelName)) {
|
||||
const { thinking, response } =
|
||||
extractThinkingAndResponseTokens(artifactContentText);
|
||||
thinkingMessage = new AIMessage({
|
||||
id: `thinking-${uuidv4()}`,
|
||||
content: thinking,
|
||||
});
|
||||
artifactContentText = response;
|
||||
}
|
||||
|
||||
const newArtifactContent: ArtifactCodeV3 = {
|
||||
index: state.artifact.contents.length + 1,
|
||||
type: "code",
|
||||
title: currentArtifactContent.title,
|
||||
// Ensure the new artifact's language is updated, if necessary
|
||||
language: state.portLanguage || currentArtifactContent.language,
|
||||
code: newArtifactValues.content as string,
|
||||
code: artifactContentText,
|
||||
};
|
||||
|
||||
const newArtifact: ArtifactV3 = {
|
||||
@@ -97,5 +117,7 @@ export const rewriteCodeArtifactTheme = async (
|
||||
|
||||
return {
|
||||
artifact: newArtifact,
|
||||
messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
_messages: [...(thinkingMessage ? [thinkingMessage] : [])],
|
||||
};
|
||||
};
|
||||
|
||||
@@ -215,6 +215,7 @@ export const getModelConfig = (
|
||||
apiKey: process.env.OPENAI_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.includes("claude-")) {
|
||||
return {
|
||||
...providerConfig,
|
||||
@@ -222,13 +223,32 @@ export const getModelConfig = (
|
||||
apiKey: process.env.ANTHROPIC_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.includes("fireworks/")) {
|
||||
let actualModelName = providerConfig.modelName;
|
||||
if (
|
||||
extra?.isToolCalling &&
|
||||
actualModelName !== "accounts/fireworks/models/llama-v3p3-70b-instruct"
|
||||
) {
|
||||
actualModelName = "accounts/fireworks/models/llama-v3p3-70b-instruct";
|
||||
}
|
||||
return {
|
||||
...providerConfig,
|
||||
modelName: actualModelName,
|
||||
modelProvider: "fireworks",
|
||||
apiKey: process.env.FIREWORKS_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.startsWith("groq/")) {
|
||||
const actualModelName = customModelName.replace("groq/", "");
|
||||
return {
|
||||
modelName: actualModelName,
|
||||
modelProvider: "groq",
|
||||
apiKey: process.env.GROQ_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.includes("gemini-")) {
|
||||
let actualModelName = providerConfig.modelName;
|
||||
if (extra?.isToolCalling && actualModelName.includes("thinking")) {
|
||||
@@ -242,6 +262,21 @@ export const getModelConfig = (
|
||||
apiKey: process.env.GOOGLE_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.includes("gemini-")) {
|
||||
let actualModelName = providerConfig.modelName;
|
||||
if (extra?.isToolCalling && actualModelName.includes("thinking")) {
|
||||
// Gemini thinking does not support tools.
|
||||
actualModelName = "gemini-2.0-flash-exp";
|
||||
}
|
||||
return {
|
||||
...providerConfig,
|
||||
modelName: actualModelName,
|
||||
modelProvider: "google-genai",
|
||||
apiKey: process.env.GOOGLE_API_KEY,
|
||||
};
|
||||
}
|
||||
|
||||
if (customModelName.startsWith("ollama-")) {
|
||||
return {
|
||||
modelName: customModelName.replace("ollama-", ""),
|
||||
|
||||
@@ -1,9 +1,16 @@
|
||||
"use client";
|
||||
|
||||
import {
|
||||
Accordion,
|
||||
AccordionContent,
|
||||
AccordionItem,
|
||||
AccordionTrigger,
|
||||
} from "@/components/ui/accordion";
|
||||
import {
|
||||
ActionBarPrimitive,
|
||||
getExternalStoreMessage,
|
||||
MessagePrimitive,
|
||||
MessageState,
|
||||
useMessage,
|
||||
} from "@assistant-ui/react";
|
||||
import React, { Dispatch, SetStateAction, type FC } from "react";
|
||||
@@ -23,12 +30,55 @@ interface AssistantMessageProps {
|
||||
setFeedbackSubmitted: Dispatch<SetStateAction<boolean>>;
|
||||
}
|
||||
|
||||
const ThinkingAssistantMessage = ({
|
||||
message,
|
||||
}: {
|
||||
message: MessageState;
|
||||
}): React.ReactElement => {
|
||||
const { id, content } = message;
|
||||
let contentText = "";
|
||||
if (typeof content === "string") {
|
||||
contentText = content;
|
||||
} else {
|
||||
const firstItem = content?.[0];
|
||||
if (firstItem?.type === "text") {
|
||||
contentText = firstItem.text;
|
||||
}
|
||||
}
|
||||
|
||||
if (contentText === "") {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Accordion
|
||||
defaultValue={`accordion-${id}`}
|
||||
type="single"
|
||||
collapsible
|
||||
className="w-full"
|
||||
>
|
||||
<AccordionItem value={`accordion-${id}`}>
|
||||
<AccordionTrigger>Thoughts</AccordionTrigger>
|
||||
<AccordionContent>{contentText}</AccordionContent>
|
||||
</AccordionItem>
|
||||
</Accordion>
|
||||
);
|
||||
};
|
||||
|
||||
export const AssistantMessage: FC<AssistantMessageProps> = ({
|
||||
runId,
|
||||
feedbackSubmitted,
|
||||
setFeedbackSubmitted,
|
||||
}) => {
|
||||
const isLast = useMessage().isLast;
|
||||
const message = useMessage();
|
||||
const { isLast } = message;
|
||||
const isThinkingMessage = message.id.startsWith("thinking-");
|
||||
|
||||
if (isThinkingMessage) {
|
||||
// TODO: This will currently cause replies to be missing.
|
||||
return <ThinkingAssistantMessage message={message} />;
|
||||
}
|
||||
|
||||
return (
|
||||
<MessagePrimitive.Root className="relative grid w-full max-w-2xl grid-cols-[auto_auto_1fr] grid-rows-[auto_1fr] py-4">
|
||||
<Avatar className="col-start-1 row-span-full row-start-1 mr-4">
|
||||
|
||||
@@ -12,12 +12,18 @@ import {
|
||||
ALL_MODELS,
|
||||
LANGCHAIN_USER_ONLY_MODELS,
|
||||
} from "@/constants";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import {
|
||||
Dispatch,
|
||||
SetStateAction,
|
||||
useCallback,
|
||||
useEffect,
|
||||
useState,
|
||||
} from "react";
|
||||
import { AlertNewModelSelectorFeature } from "./alert-new-model-selector";
|
||||
import { ModelConfigPanel } from "./model-config-pannel";
|
||||
import { IsNewBadge } from "./new-badge";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { CustomModelConfig } from "@/types";
|
||||
import { CustomModelConfig, ModelConfigurationParams } from "@/types";
|
||||
import { CaretSortIcon, GearIcon } from "@radix-ui/react-icons";
|
||||
import {
|
||||
Popover,
|
||||
@@ -40,6 +46,71 @@ interface ModelSelectorProps {
|
||||
modelConfigs: Record<string, CustomModelConfig>;
|
||||
}
|
||||
|
||||
interface CommandModelItemProps {
|
||||
model: ModelConfigurationParams;
|
||||
handleModelChange: (newModel: ALL_MODEL_NAMES) => Promise<void>;
|
||||
selectedModelName: ALL_MODEL_NAMES;
|
||||
openConfigModelId: string | undefined;
|
||||
config: CustomModelConfig;
|
||||
setOpenConfigModelId: Dispatch<SetStateAction<string | undefined>>;
|
||||
setModelConfig: (
|
||||
modelName: ALL_MODEL_NAMES,
|
||||
config: CustomModelConfig
|
||||
) => void;
|
||||
}
|
||||
|
||||
function CommandModelItem({
|
||||
model,
|
||||
handleModelChange,
|
||||
selectedModelName,
|
||||
openConfigModelId,
|
||||
config,
|
||||
setOpenConfigModelId,
|
||||
setModelConfig,
|
||||
}: CommandModelItemProps) {
|
||||
return (
|
||||
<CommandItem
|
||||
value={model.name}
|
||||
onSelect={handleModelChange}
|
||||
className="flex items-center"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-1 size-4",
|
||||
selectedModelName === model.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
/>
|
||||
<span className="flex flex-row w-full items-center justify-start gap-2">
|
||||
{model.label}
|
||||
{model.isNew && <IsNewBadge />}
|
||||
</span>
|
||||
|
||||
{openConfigModelId === model.name ? (
|
||||
<ModelConfigPanel
|
||||
model={model}
|
||||
modelConfig={config}
|
||||
isOpen={true}
|
||||
onOpenChange={(open) =>
|
||||
setOpenConfigModelId(open ? model.name : undefined)
|
||||
}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
className="ml-auto flex-shrink-0 flex size-6 items-center justify-center focus:outline-none focus:ring-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenConfigModelId(model.name);
|
||||
}}
|
||||
>
|
||||
<GearIcon className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
</CommandItem>
|
||||
);
|
||||
}
|
||||
|
||||
export default function ModelSelector({
|
||||
chatStarted,
|
||||
modelName,
|
||||
@@ -51,8 +122,7 @@ export default function ModelSelector({
|
||||
const [isLangChainUser, setIsLangChainUser] = useState(false);
|
||||
const [showAlert, setShowAlert] = useState(false);
|
||||
const [open, setOpen] = useState(false);
|
||||
const [openConfigModelId, setOpenConfigModelId] =
|
||||
useState<ALL_MODEL_NAMES | null>(null);
|
||||
const [openConfigModelId, setOpenConfigModelId] = useState<ALL_MODEL_NAMES>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!user) return;
|
||||
@@ -112,6 +182,13 @@ export default function ModelSelector({
|
||||
return false;
|
||||
}
|
||||
|
||||
if (
|
||||
model.name.includes("groq/") &&
|
||||
process.env.NEXT_PUBLIC_GROQ_ENABLED === "false"
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// By default, return true if the environment variable is not set or is set to true
|
||||
return true;
|
||||
});
|
||||
@@ -122,6 +199,28 @@ export default function ModelSelector({
|
||||
(m) => m.name === modelName && m.isNew
|
||||
);
|
||||
|
||||
const azureModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "azure_openai"
|
||||
);
|
||||
const openaiModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "openai"
|
||||
);
|
||||
const ollamaModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "ollama"
|
||||
);
|
||||
const anthropicModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "anthropic"
|
||||
);
|
||||
const genAiModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "google-genai"
|
||||
);
|
||||
const fireworksModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "fireworks"
|
||||
);
|
||||
const groqModelGroup = allAllowedModels.filter(
|
||||
(m) => m.config.provider === "groq"
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
@@ -147,55 +246,146 @@ export default function ModelSelector({
|
||||
<PopoverContent className="min-w-[180px] w-[280px] p-0 shadow-md rounded-md">
|
||||
<Command>
|
||||
<CommandList>
|
||||
{allAllowedModels.map((model) => {
|
||||
const config =
|
||||
modelConfigs[model.name] ||
|
||||
modelConfigs[model.name.replace("azure/", "")];
|
||||
|
||||
return (
|
||||
<CommandGroup key={model.name} className="w-full">
|
||||
<CommandItem
|
||||
value={model.name}
|
||||
onSelect={handleModelChange}
|
||||
className="flex items-center"
|
||||
>
|
||||
<Check
|
||||
className={cn(
|
||||
"mr-1 size-4",
|
||||
modelName === model.name ? "opacity-100" : "opacity-0"
|
||||
)}
|
||||
{openaiModelGroup.length > 0 && (
|
||||
<CommandGroup heading="OpenAI" className="w-full">
|
||||
{openaiModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
<span className="flex flex-row w-full items-center justify-start gap-2">
|
||||
{model.label}
|
||||
{model.isNew && <IsNewBadge />}
|
||||
</span>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{openConfigModelId === model.name ? (
|
||||
<ModelConfigPanel
|
||||
model={model}
|
||||
modelConfig={config}
|
||||
isOpen={true}
|
||||
onOpenChange={(open) =>
|
||||
setOpenConfigModelId(open ? model.name : null)
|
||||
}
|
||||
onClick={(e) => e.stopPropagation()}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
) : (
|
||||
<button
|
||||
className="ml-auto flex-shrink-0 flex size-6 items-center justify-center focus:outline-none focus:ring-0"
|
||||
onClick={(e) => {
|
||||
e.stopPropagation();
|
||||
setOpenConfigModelId(model.name);
|
||||
}}
|
||||
>
|
||||
<GearIcon className="size-4" />
|
||||
</button>
|
||||
)}
|
||||
</CommandItem>
|
||||
</CommandGroup>
|
||||
);
|
||||
})}
|
||||
{azureModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Azure OpenAI" className="w-full">
|
||||
{azureModelGroup.map((model) => {
|
||||
const config =
|
||||
modelConfigs[model.name.replace("azure/", "")];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{anthropicModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Anthropic" className="w-full">
|
||||
{anthropicModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{genAiModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Google GenAI" className="w-full">
|
||||
{genAiModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{groqModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Groq" className="w-full">
|
||||
{groqModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{fireworksModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Fireworks" className="w-full">
|
||||
{fireworksModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
|
||||
{ollamaModelGroup.length > 0 && (
|
||||
<CommandGroup heading="Ollama" className="w-full">
|
||||
{ollamaModelGroup.map((model) => {
|
||||
const config = modelConfigs[model.name];
|
||||
return (
|
||||
<CommandModelItem
|
||||
key={model.name}
|
||||
model={model}
|
||||
handleModelChange={handleModelChange}
|
||||
config={config}
|
||||
selectedModelName={modelName}
|
||||
openConfigModelId={openConfigModelId}
|
||||
setOpenConfigModelId={setOpenConfigModelId}
|
||||
setModelConfig={setModelConfig}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
</CommandGroup>
|
||||
)}
|
||||
</CommandList>
|
||||
</Command>
|
||||
</PopoverContent>
|
||||
|
||||
@@ -238,7 +238,7 @@ export function ThreadHistoryComponent(props: ThreadHistoryProps) {
|
||||
<Sheet open={open} onOpenChange={setOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<TooltipIconButton
|
||||
tooltip="New chat"
|
||||
tooltip="History"
|
||||
variant="ghost"
|
||||
className="w-fit h-fit p-2"
|
||||
>
|
||||
|
||||
@@ -68,6 +68,7 @@ export const Thread: FC<ThreadProps> = (props: ThreadProps) => {
|
||||
} = useThreadContext();
|
||||
const { user } = useUserContext();
|
||||
|
||||
// Render the LangSmith trace link
|
||||
useLangSmithLinkToolUI();
|
||||
|
||||
const handleCreateThread = async () => {
|
||||
@@ -98,7 +99,9 @@ export const Thread: FC<ThreadProps> = (props: ThreadProps) => {
|
||||
setModelConfig(modelName, modelConfig);
|
||||
clearState();
|
||||
setChatStarted(false);
|
||||
const thread = await searchOrCreateThread();
|
||||
// Set `true` for `isNewThread` because we want to create a new thread
|
||||
// if the existing one has values.
|
||||
const thread = await searchOrCreateThread(true);
|
||||
if (!thread) {
|
||||
toast({
|
||||
title: "Failed to create a new thread",
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
"use client";
|
||||
|
||||
import * as React from "react";
|
||||
import * as AccordionPrimitive from "@radix-ui/react-accordion";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ChevronDownIcon } from "@radix-ui/react-icons";
|
||||
|
||||
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-sm font-medium transition-all hover:underline text-left [&[data-state=open]>svg]:rotate-180",
|
||||
className
|
||||
)}
|
||||
{...props}
|
||||
>
|
||||
{children}
|
||||
<ChevronDownIcon className="h-4 w-4 shrink-0 text-muted-foreground transition-transform duration-200" />
|
||||
</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="overflow-hidden text-sm data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
|
||||
{...props}
|
||||
>
|
||||
<div className={cn("pb-4 pt-0", className)}>{children}</div>
|
||||
</AccordionPrimitive.Content>
|
||||
));
|
||||
AccordionContent.displayName = AccordionPrimitive.Content.displayName;
|
||||
|
||||
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
|
||||
+52
-4
@@ -246,9 +246,29 @@ export const ANTHROPIC_MODELS = [
|
||||
];
|
||||
|
||||
export const FIREWORKS_MODELS: ModelConfigurationParams[] = [
|
||||
{
|
||||
name: "accounts/fireworks/models/llama-v3p3-70b-instruct",
|
||||
label: "Llama 3.3 70B",
|
||||
config: {
|
||||
provider: "fireworks",
|
||||
temperatureRange: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
default: 0.5,
|
||||
current: 0.5,
|
||||
},
|
||||
maxTokens: {
|
||||
min: 1,
|
||||
max: 16384,
|
||||
default: 4096,
|
||||
current: 4096,
|
||||
},
|
||||
},
|
||||
isNew: true,
|
||||
},
|
||||
{
|
||||
name: "accounts/fireworks/models/llama-v3p1-70b-instruct",
|
||||
label: "Llama 70B (Fireworks)",
|
||||
label: "Llama 70B (old)",
|
||||
config: {
|
||||
provider: "fireworks",
|
||||
temperatureRange: {
|
||||
@@ -268,7 +288,7 @@ export const FIREWORKS_MODELS: ModelConfigurationParams[] = [
|
||||
},
|
||||
{
|
||||
name: "accounts/fireworks/models/deepseek-v3",
|
||||
label: "DeepSeek V3 (Fireworks)",
|
||||
label: "DeepSeek V3",
|
||||
config: {
|
||||
provider: "fireworks",
|
||||
temperatureRange: {
|
||||
@@ -288,7 +308,7 @@ export const FIREWORKS_MODELS: ModelConfigurationParams[] = [
|
||||
},
|
||||
{
|
||||
name: "accounts/fireworks/models/deepseek-r1",
|
||||
label: "DeepSeek R1 (Fireworks)",
|
||||
label: "DeepSeek R1",
|
||||
config: {
|
||||
provider: "fireworks",
|
||||
temperatureRange: {
|
||||
@@ -308,6 +328,29 @@ export const FIREWORKS_MODELS: ModelConfigurationParams[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export const GROQ_MODELS: ModelConfigurationParams[] = [
|
||||
{
|
||||
name: "groq/deepseek-r1-distill-llama-70b",
|
||||
label: "DeepSeek R1 Llama 70b Distill",
|
||||
config: {
|
||||
provider: "groq",
|
||||
temperatureRange: {
|
||||
min: 0,
|
||||
max: 1,
|
||||
default: 0.5,
|
||||
current: 0.5,
|
||||
},
|
||||
maxTokens: {
|
||||
min: 1,
|
||||
max: 8000,
|
||||
default: 4096,
|
||||
current: 4096,
|
||||
},
|
||||
},
|
||||
isNew: true,
|
||||
},
|
||||
];
|
||||
|
||||
export const GEMINI_MODELS: ModelConfigurationParams[] = [
|
||||
{
|
||||
name: "gemini-1.5-flash",
|
||||
@@ -392,6 +435,8 @@ export const NON_STREAMING_TEXT_MODELS = [
|
||||
"o1",
|
||||
"gemini-2.0-flash-thinking-exp-01-21",
|
||||
];
|
||||
// Models which preform CoT before generating a final response.
|
||||
export const THINKING_MODELS = ["accounts/fireworks/models/deepseek-r1"];
|
||||
|
||||
export const ALL_MODELS: ModelConfigurationParams[] = [
|
||||
...OPENAI_MODELS,
|
||||
@@ -400,6 +445,7 @@ export const ALL_MODELS: ModelConfigurationParams[] = [
|
||||
...GEMINI_MODELS,
|
||||
...AZURE_MODELS,
|
||||
...OLLAMA_MODELS,
|
||||
...GROQ_MODELS,
|
||||
];
|
||||
|
||||
export type OPENAI_MODEL_NAMES = (typeof OPENAI_MODELS)[number]["name"];
|
||||
@@ -408,13 +454,15 @@ export type FIREWORKS_MODEL_NAMES = (typeof FIREWORKS_MODELS)[number]["name"];
|
||||
export type GEMINI_MODEL_NAMES = (typeof GEMINI_MODELS)[number]["name"];
|
||||
export type AZURE_MODEL_NAMES = (typeof AZURE_MODELS)[number]["name"];
|
||||
export type OLLAMA_MODEL_NAMES = (typeof OLLAMA_MODELS)[number]["name"];
|
||||
export type GROQ_MODEL_NAMES = (typeof GROQ_MODELS)[number]["name"];
|
||||
export type ALL_MODEL_NAMES =
|
||||
| OPENAI_MODEL_NAMES
|
||||
| ANTHROPIC_MODEL_NAMES
|
||||
| FIREWORKS_MODEL_NAMES
|
||||
| GEMINI_MODEL_NAMES
|
||||
| AZURE_MODEL_NAMES
|
||||
| OLLAMA_MODEL_NAMES;
|
||||
| OLLAMA_MODEL_NAMES
|
||||
| GROQ_MODEL_NAMES;
|
||||
|
||||
export const DEFAULT_MODEL_NAME: ALL_MODEL_NAMES = OPENAI_MODELS[0].name;
|
||||
export const DEFAULT_MODEL_CONFIG: CustomModelConfig = {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { v4 as uuidv4 } from "uuid";
|
||||
import { useUserContext } from "@/contexts/UserContext";
|
||||
import {
|
||||
isArtifactCodeContent,
|
||||
@@ -46,6 +47,8 @@ import {
|
||||
import {
|
||||
convertToArtifactV3,
|
||||
handleGenerateArtifactToolCallChunk,
|
||||
handleRewriteArtifactThinkingModel,
|
||||
isThinkingModel,
|
||||
removeCodeBlockFormatting,
|
||||
replaceOrInsertMessageChunk,
|
||||
updateHighlightedCode,
|
||||
@@ -374,7 +377,8 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
// The root level run ID of this stream
|
||||
let runId = "";
|
||||
let followupMessageId = "";
|
||||
// let lastMessage: AIMessage | undefined = undefined;
|
||||
// The ID of the message containing the thinking content.
|
||||
let thinkingMessageId = "";
|
||||
|
||||
try {
|
||||
const stream = client.runs.stream(
|
||||
@@ -418,12 +422,17 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
// Whether or not the first update has been made when updating highlighted code.
|
||||
let isFirstUpdate = true;
|
||||
|
||||
// The new text of an artifact that is being rewritten
|
||||
// The full text content of an artifact that is being rewritten.
|
||||
// This may include thinking tokens if the model generates them.
|
||||
let fullNewArtifactContent = "";
|
||||
// The response text ONLY of the artifact that is being rewritten.
|
||||
let newArtifactContent = "";
|
||||
|
||||
// The updated full markdown text when using the highlight update tool
|
||||
let highlightedText: TextHighlight | undefined = undefined;
|
||||
|
||||
// The message ID of the message containing the thinking content.
|
||||
|
||||
for await (const chunk of stream) {
|
||||
if (chunk.event === "error") {
|
||||
const errorMessage =
|
||||
@@ -669,9 +678,22 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
return;
|
||||
}
|
||||
|
||||
newArtifactContent +=
|
||||
fullNewArtifactContent +=
|
||||
extractStreamDataChunk(chunk.data.data.chunk)?.content || "";
|
||||
|
||||
if (isThinkingModel(threadData.modelName)) {
|
||||
if (!thinkingMessageId) {
|
||||
thinkingMessageId = `thinking-${uuidv4()}`;
|
||||
}
|
||||
newArtifactContent = handleRewriteArtifactThinkingModel({
|
||||
newArtifactContent: fullNewArtifactContent,
|
||||
setMessages,
|
||||
thinkingMessageId,
|
||||
});
|
||||
} else {
|
||||
newArtifactContent = fullNewArtifactContent;
|
||||
}
|
||||
|
||||
// Ensure we have the language to update the artifact with
|
||||
let artifactLanguage = params.portLanguage || undefined;
|
||||
if (
|
||||
@@ -749,9 +771,23 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
return;
|
||||
}
|
||||
|
||||
newArtifactContent +=
|
||||
fullNewArtifactContent +=
|
||||
extractStreamDataChunk(chunk.data.data.chunk)?.content || "";
|
||||
|
||||
if (isThinkingModel(threadData.modelName)) {
|
||||
if (!thinkingMessageId) {
|
||||
thinkingMessageId = `thinking-${uuidv4()}`;
|
||||
}
|
||||
console.log("Extracting thinking message");
|
||||
newArtifactContent = handleRewriteArtifactThinkingModel({
|
||||
newArtifactContent: fullNewArtifactContent,
|
||||
setMessages,
|
||||
thinkingMessageId,
|
||||
});
|
||||
} else {
|
||||
newArtifactContent = fullNewArtifactContent;
|
||||
}
|
||||
|
||||
// Ensure we have the language to update the artifact with
|
||||
const artifactLanguage =
|
||||
params.portLanguage ||
|
||||
@@ -820,7 +856,7 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
|
||||
const message = extractStreamDataOutput(chunk.data.data.output);
|
||||
|
||||
newArtifactContent += message.content || "";
|
||||
fullNewArtifactContent += message.content || "";
|
||||
|
||||
// Ensure we have the language to update the artifact with
|
||||
let artifactLanguage = params.portLanguage || undefined;
|
||||
@@ -846,7 +882,7 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
throw new Error("No artifact found when updating markdown");
|
||||
}
|
||||
|
||||
let content = newArtifactContent;
|
||||
let content = fullNewArtifactContent;
|
||||
if (!rewriteArtifactMeta) {
|
||||
console.error(
|
||||
"No rewrite artifact meta found when updating artifact"
|
||||
@@ -1074,7 +1110,7 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
return;
|
||||
}
|
||||
const message = extractStreamDataOutput(chunk.data.data.output);
|
||||
newArtifactContent += message?.content || "";
|
||||
fullNewArtifactContent += message?.content || "";
|
||||
|
||||
// Ensure we have the language to update the artifact with
|
||||
const artifactLanguage =
|
||||
@@ -1099,7 +1135,7 @@ export function GraphProvider({ children }: { children: ReactNode }) {
|
||||
throw new Error("No artifact found when updating markdown");
|
||||
}
|
||||
|
||||
let content = newArtifactContent;
|
||||
let content = fullNewArtifactContent;
|
||||
if (artifactType === "code") {
|
||||
content = removeCodeBlockFormatting(content);
|
||||
}
|
||||
|
||||
@@ -25,7 +25,7 @@ type ThreadContentType = {
|
||||
modelConfigs: Record<ALL_MODEL_NAMES, CustomModelConfig>;
|
||||
createThreadLoading: boolean;
|
||||
clearThreadsWithNoValues: () => Promise<void>;
|
||||
searchOrCreateThread: () => Promise<Thread | undefined>;
|
||||
searchOrCreateThread: (isNewThread?: boolean) => Promise<Thread | undefined>;
|
||||
getUserThreads: () => Promise<void>;
|
||||
deleteThread: (id: string, clearMessages: () => void) => Promise<void>;
|
||||
setThreadId: (id: string) => void;
|
||||
@@ -87,7 +87,7 @@ export function ThreadProvider({ children }: { children: ReactNode }) {
|
||||
});
|
||||
|
||||
const modelConfig = useMemo(() => {
|
||||
// Try exact match first, then try without azure/ prefix
|
||||
// Try exact match first, then try without "azure/" or "groq/" prefixes
|
||||
return (
|
||||
modelConfigs[modelName] || modelConfigs[modelName.replace("azure/", "")]
|
||||
);
|
||||
@@ -215,7 +215,7 @@ export function ThreadProvider({ children }: { children: ReactNode }) {
|
||||
}
|
||||
};
|
||||
|
||||
const searchOrCreateThread = async () => {
|
||||
const searchOrCreateThread = async (isNewThread?: boolean) => {
|
||||
const storedThreadId =
|
||||
searchParams.get(THREAD_ID_QUERY_PARAM) ||
|
||||
getCookie(THREAD_ID_COOKIE_NAME);
|
||||
@@ -228,8 +228,15 @@ export function ThreadProvider({ children }: { children: ReactNode }) {
|
||||
// Thread ID is in cookies.
|
||||
const thread = await getThreadById(storedThreadId);
|
||||
if (thread) {
|
||||
setThreadId(storedThreadId);
|
||||
return thread;
|
||||
const isEmptyThread =
|
||||
!thread.values || Object.keys(thread.values).length === 0;
|
||||
const shouldUseExistingThread =
|
||||
!isNewThread || (isNewThread && isEmptyThread);
|
||||
|
||||
if (shouldUseExistingThread) {
|
||||
setThreadId(storedThreadId);
|
||||
return thread;
|
||||
}
|
||||
}
|
||||
|
||||
// Current thread has activity. Create a new thread.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { THINKING_MODELS } from "@/constants";
|
||||
import { cleanContent } from "@/lib/normalize_string";
|
||||
import {
|
||||
Artifact,
|
||||
@@ -14,6 +15,7 @@ import {
|
||||
BaseMessageChunk,
|
||||
} from "@langchain/core/messages";
|
||||
import { parsePartialJson } from "@langchain/core/output_parsers";
|
||||
import { Dispatch, SetStateAction } from "react";
|
||||
|
||||
export function removeCodeBlockFormatting(text: string): string {
|
||||
if (!text) return text;
|
||||
@@ -408,3 +410,133 @@ export function handleGenerateArtifactToolCallChunk(toolCallChunkArgs: string) {
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
type ThinkingAndResponseTokens = {
|
||||
thinking: string;
|
||||
response: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extracts thinking and response content from a text string containing XML-style think tags.
|
||||
* Designed to handle streaming text where tags might be incomplete.
|
||||
*
|
||||
* @param text - The input text that may contain <think> tags
|
||||
* @returns An object containing:
|
||||
* - thinking: Content between <think> tags, or all content after <think> if no closing tag
|
||||
* - response: All text outside of think tags
|
||||
*
|
||||
* @example
|
||||
* // Complete tags
|
||||
* extractThinkingAndResponseTokens('Hello <think>processing...</think>world')
|
||||
* // Returns: { thinking: 'processing...', response: 'Hello world' }
|
||||
*
|
||||
* // Streaming/incomplete tags
|
||||
* extractThinkingAndResponseTokens('Hello <think>processing...')
|
||||
* // Returns: { thinking: 'processing...', response: 'Hello ' }
|
||||
*
|
||||
* // No tags
|
||||
* extractThinkingAndResponseTokens('Hello world')
|
||||
* // Returns: { thinking: '', response: 'Hello world' }
|
||||
*/
|
||||
export function extractThinkingAndResponseTokens(
|
||||
text: string
|
||||
): ThinkingAndResponseTokens {
|
||||
const thinkStartTag = "<think>";
|
||||
const thinkEndTag = "</think>";
|
||||
|
||||
const startIndex = text.indexOf(thinkStartTag);
|
||||
|
||||
// No thinking tag found
|
||||
if (startIndex === -1) {
|
||||
return {
|
||||
thinking: "",
|
||||
response: text.trim(),
|
||||
};
|
||||
}
|
||||
|
||||
const afterStartTag = text.substring(startIndex + thinkStartTag.length);
|
||||
const endIndex = afterStartTag.indexOf(thinkEndTag);
|
||||
|
||||
// If no closing tag, all remaining text is thinking
|
||||
if (endIndex === -1) {
|
||||
return {
|
||||
thinking: afterStartTag.trim(),
|
||||
response: text.substring(0, startIndex).trim(),
|
||||
};
|
||||
}
|
||||
|
||||
// We have both opening and closing tags
|
||||
const thinking = afterStartTag.substring(0, endIndex).trim();
|
||||
const response = (
|
||||
text.substring(0, startIndex) +
|
||||
afterStartTag.substring(endIndex + thinkEndTag.length)
|
||||
).trim();
|
||||
|
||||
return {
|
||||
thinking,
|
||||
response,
|
||||
};
|
||||
}
|
||||
|
||||
type HandleRewriteArtifactThinkingModelParams = {
|
||||
newArtifactContent: string;
|
||||
setMessages: Dispatch<SetStateAction<BaseMessage[]>>;
|
||||
thinkingMessageId: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Handles the rewriting of artifact content by processing thinking tokens and updating messages state.
|
||||
* This function extracts thinking and response tokens from the new artifact content, updates the message
|
||||
* state with thinking tokens if present, and returns the response content.
|
||||
*
|
||||
* @param {Object} params - The parameters for handling artifact rewriting
|
||||
* @param {string} params.newArtifactContent - The new content to process for the artifact
|
||||
* @param {Dispatch<SetStateAction<BaseMessage[]>>} params.setMessages - State setter function for updating messages
|
||||
* @param {string} params.thinkingMessageId - Unique identifier for the thinking message to update or create
|
||||
* @returns {string} The extracted response content from the artifact
|
||||
*/
|
||||
export function handleRewriteArtifactThinkingModel({
|
||||
newArtifactContent,
|
||||
setMessages,
|
||||
thinkingMessageId,
|
||||
}: HandleRewriteArtifactThinkingModelParams): string {
|
||||
const { thinking, response } =
|
||||
extractThinkingAndResponseTokens(newArtifactContent);
|
||||
|
||||
if (thinking.length > 0) {
|
||||
setMessages((prevMessages) => {
|
||||
if (!thinkingMessageId) {
|
||||
console.error("Thinking message not found");
|
||||
return prevMessages;
|
||||
}
|
||||
|
||||
const prevHasThinkingMsg = prevMessages.some(
|
||||
(msg) => msg.id === thinkingMessageId
|
||||
);
|
||||
|
||||
const thinkingMessage = new AIMessage({
|
||||
id: thinkingMessageId,
|
||||
content: thinking,
|
||||
});
|
||||
|
||||
if (prevHasThinkingMsg) {
|
||||
// The message exists, so replace it
|
||||
const newMsgs = prevMessages.map((msg) => {
|
||||
if (msg.id !== thinkingMessageId) {
|
||||
return msg;
|
||||
}
|
||||
return thinkingMessage;
|
||||
});
|
||||
return newMsgs;
|
||||
}
|
||||
// The message does not yet exist, so create it:
|
||||
return [...prevMessages, thinkingMessage];
|
||||
});
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export function isThinkingModel(model: string): boolean {
|
||||
return THINKING_MODELS.some((m) => m === model);
|
||||
}
|
||||
|
||||
+75
-57
@@ -9,34 +9,52 @@ const config: Config = {
|
||||
],
|
||||
theme: {
|
||||
extend: {
|
||||
keyframes: {
|
||||
'gradient-xy-enhanced': {
|
||||
'0%, 100%': {
|
||||
'background-size': '400% 400%',
|
||||
'background-position': 'left center',
|
||||
'transform': 'rotate(-3deg)'
|
||||
},
|
||||
'50%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'right center',
|
||||
'transform': 'rotate(3deg)'
|
||||
}
|
||||
},
|
||||
'gradient-x': {
|
||||
'0%, 100%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'left center',
|
||||
},
|
||||
'50%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'right center',
|
||||
},
|
||||
},
|
||||
},
|
||||
animation: {
|
||||
'gradient-xy-enhanced': 'gradient-xy-enhanced 15s ease infinite',
|
||||
'gradient-x': 'gradient-x 3s ease-in-out infinite',
|
||||
},
|
||||
keyframes: {
|
||||
'gradient-xy-enhanced': {
|
||||
'0%, 100%': {
|
||||
'background-size': '400% 400%',
|
||||
'background-position': 'left center',
|
||||
transform: 'rotate(-3deg)'
|
||||
},
|
||||
'50%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'right center',
|
||||
transform: 'rotate(3deg)'
|
||||
}
|
||||
},
|
||||
'gradient-x': {
|
||||
'0%, 100%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'left center'
|
||||
},
|
||||
'50%': {
|
||||
'background-size': '200% 200%',
|
||||
'background-position': 'right center'
|
||||
}
|
||||
},
|
||||
'accordion-down': {
|
||||
from: {
|
||||
height: '0'
|
||||
},
|
||||
to: {
|
||||
height: 'var(--radix-accordion-content-height)'
|
||||
}
|
||||
},
|
||||
'accordion-up': {
|
||||
from: {
|
||||
height: 'var(--radix-accordion-content-height)'
|
||||
},
|
||||
to: {
|
||||
height: '0'
|
||||
}
|
||||
}
|
||||
},
|
||||
animation: {
|
||||
'gradient-xy-enhanced': 'gradient-xy-enhanced 15s ease infinite',
|
||||
'gradient-x': 'gradient-x 3s ease-in-out infinite',
|
||||
'accordion-down': 'accordion-down 0.2s ease-out',
|
||||
'accordion-up': 'accordion-up 0.2s ease-out'
|
||||
},
|
||||
backgroundImage: {
|
||||
'gradient-radial': 'radial-gradient(var(--tw-gradient-stops))',
|
||||
'gradient-conic': 'conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))'
|
||||
@@ -88,35 +106,35 @@ const config: Config = {
|
||||
'5': 'hsl(var(--chart-5))'
|
||||
}
|
||||
},
|
||||
fontFamily: {
|
||||
mono: [
|
||||
`"Fira Code"`,
|
||||
`ui-monospace`,
|
||||
`SFMono-Regular`,
|
||||
`Menlo`,
|
||||
`Monaco`,
|
||||
`Consolas`,
|
||||
`"Liberation Mono"`,
|
||||
`"Courier New"`,
|
||||
`monospace`,
|
||||
],
|
||||
sans: [
|
||||
"Inter",
|
||||
"-apple-system",
|
||||
"BlinkMacSystemFont",
|
||||
'Segoe UI',
|
||||
"Roboto",
|
||||
"Helvetica",
|
||||
"Arial",
|
||||
"sans-serif",
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji',
|
||||
'Segoe UI Symbol',
|
||||
],
|
||||
},
|
||||
letterSpacing: {
|
||||
tighter: '-0.04em',
|
||||
},
|
||||
fontFamily: {
|
||||
mono: [
|
||||
'`"Fira Code"`',
|
||||
'`ui-monospace`',
|
||||
'`SFMono-Regular`',
|
||||
'`Menlo`',
|
||||
'`Monaco`',
|
||||
'`Consolas`',
|
||||
'`"Liberation Mono"`',
|
||||
'`"Courier New"`',
|
||||
'`monospace`'
|
||||
],
|
||||
sans: [
|
||||
'Inter',
|
||||
'-apple-system',
|
||||
'BlinkMacSystemFont',
|
||||
'Segoe UI',
|
||||
'Roboto',
|
||||
'Helvetica',
|
||||
'Arial',
|
||||
'sans-serif',
|
||||
'Apple Color Emoji',
|
||||
'Segoe UI Emoji',
|
||||
'Segoe UI Symbol'
|
||||
]
|
||||
},
|
||||
letterSpacing: {
|
||||
tighter: '-0.04em'
|
||||
}
|
||||
}
|
||||
},
|
||||
plugins: [
|
||||
|
||||
@@ -881,6 +881,16 @@
|
||||
"@google/generative-ai" "^0.21.0"
|
||||
zod-to-json-schema "^3.22.4"
|
||||
|
||||
"@langchain/groq@^0.1.3":
|
||||
version "0.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/groq/-/groq-0.1.3.tgz#02a554c4cb779b4b5087d8e9b497f634012f7092"
|
||||
integrity sha512-dMzvBVaLf/0IQoHdAOAN8W/PbOcwgbvgUMCn02CqvCC90mxZ45LI0Tipzqnoaam0hiKALR5hLc3dNj1oCYV92w==
|
||||
dependencies:
|
||||
"@langchain/openai" "~0.3.0"
|
||||
groq-sdk "^0.5.0"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.5"
|
||||
|
||||
"@langchain/langgraph-checkpoint@~0.0.13":
|
||||
version "0.0.13"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/langgraph-checkpoint/-/langgraph-checkpoint-0.0.13.tgz#b349f1518184e1b154227d1fead2bec1e27707fd"
|
||||
@@ -936,6 +946,16 @@
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
"@langchain/openai@~0.3.0":
|
||||
version "0.3.17"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.3.17.tgz#5b8613ac8d849da90f3ecd8368ae7389fca4ee13"
|
||||
integrity sha512-uw4po32OKptVjq+CYHrumgbfh4NuD7LqyE+ZgqY9I/LrLc6bHLMc+sisHmI17vgek0K/yqtarI0alPJbzrwyag==
|
||||
dependencies:
|
||||
js-tiktoken "^1.0.12"
|
||||
openai "^4.77.0"
|
||||
zod "^3.22.4"
|
||||
zod-to-json-schema "^3.22.3"
|
||||
|
||||
"@langchain/textsplitters@>=0.0.0 <0.2.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.1.0.tgz#f37620992192df09ecda3dfbd545b36a6bcbae46"
|
||||
@@ -1215,6 +1235,21 @@
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.1.tgz#fc169732d755c7fbad33ba8d0cd7fd10c90dc8e3"
|
||||
integrity sha512-SJ31y+Q/zAyShtXJc8x83i9TYdbAfHZ++tUZnvjJJqFjzsdUnKsxPL6IEtBlxKkU7yzer//GQtZSV4GbldL3YA==
|
||||
|
||||
"@radix-ui/react-accordion@^1.2.2":
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.2.tgz#96ac3de896189553219e342d5e773589eb119dce"
|
||||
integrity sha512-b1oh54x4DMCdGsB4/7ahiSrViXxaBwRPotiZNnYXjLha9vfuURSAZErki6qjDoSIV0eXx5v57XnTGVtGwnfp2g==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-collapsible" "1.1.2"
|
||||
"@radix-ui/react-collection" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-direction" "1.1.0"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
|
||||
"@radix-ui/react-arrow@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.1.0.tgz#744f388182d360b86285217e43b6c63633f39e7a"
|
||||
@@ -1253,6 +1288,20 @@
|
||||
"@radix-ui/react-use-previous" "1.1.0"
|
||||
"@radix-ui/react-use-size" "1.1.0"
|
||||
|
||||
"@radix-ui/react-collapsible@1.1.2":
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.2.tgz#42477c428bb0d2eec35b9b47601c5ff0a6210165"
|
||||
integrity sha512-PliMB63vxz7vggcyq0IxNYk8vGDrLXVWw4+W4B8YnwI1s18x7YZYqlG9PLX7XxAJUi0g2DxP4XKJMFHh/iVh9A==
|
||||
dependencies:
|
||||
"@radix-ui/primitive" "1.1.1"
|
||||
"@radix-ui/react-compose-refs" "1.1.1"
|
||||
"@radix-ui/react-context" "1.1.1"
|
||||
"@radix-ui/react-id" "1.1.0"
|
||||
"@radix-ui/react-presence" "1.1.2"
|
||||
"@radix-ui/react-primitive" "2.0.1"
|
||||
"@radix-ui/react-use-controllable-state" "1.1.0"
|
||||
"@radix-ui/react-use-layout-effect" "1.1.0"
|
||||
|
||||
"@radix-ui/react-collection@1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed"
|
||||
@@ -4573,6 +4622,20 @@ groq-sdk@^0.13.0:
|
||||
formdata-node "^4.3.2"
|
||||
node-fetch "^2.6.7"
|
||||
|
||||
groq-sdk@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/groq-sdk/-/groq-sdk-0.5.0.tgz#8eefea81c3709e815c96dffa941200e85a50cf19"
|
||||
integrity sha512-RVmhW7qZ+XZoy5fIuSdx/LGQJONpL8MHgZEW7dFwTdgkzStub2XQx6OKv28CHogijdwH41J+Npj/z2jBPu3vmw==
|
||||
dependencies:
|
||||
"@types/node" "^18.11.18"
|
||||
"@types/node-fetch" "^2.6.4"
|
||||
abort-controller "^3.0.0"
|
||||
agentkeepalive "^4.2.1"
|
||||
form-data-encoder "1.7.2"
|
||||
formdata-node "^4.3.2"
|
||||
node-fetch "^2.6.7"
|
||||
web-streams-polyfill "^3.2.1"
|
||||
|
||||
has-bigints@^1.0.1, has-bigints@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/has-bigints/-/has-bigints-1.0.2.tgz#0871bd3e3d51626f6ca0966668ba35d5602d6eaa"
|
||||
@@ -8998,6 +9061,11 @@ web-streams-polyfill@4.0.0-beta.3:
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38"
|
||||
integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug==
|
||||
|
||||
web-streams-polyfill@^3.2.1:
|
||||
version "3.3.3"
|
||||
resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz#2073b91a2fdb1fbfbd401e7de0ac9f8214cecb4b"
|
||||
integrity sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
|
||||
Reference in New Issue
Block a user