mirror of
https://github.com/Sea-Haven-Industries/open-swe.git
synced 2026-10-06 19:22:13 +00:00
parent
b3bcc46c59
commit
1c12f6f754
5 changed files with 63 additions and 70 deletions
|
|
@ -13,12 +13,13 @@ import {
|
||||||
SelectValue,
|
SelectValue,
|
||||||
} from "@/components/ui/select";
|
} from "@/components/ui/select";
|
||||||
import { Label } from "@/components/ui/label";
|
import { Label } from "@/components/ui/label";
|
||||||
import { useConfigStore } from "@/hooks/use-config-store";
|
import { DEFAULT_CONFIG_KEY, useConfigStore } from "@/hooks/use-config-store";
|
||||||
import { AlertCircle } from "lucide-react";
|
import { AlertCircle } from "lucide-react";
|
||||||
import { Alert, AlertDescription } from "@/components/ui/alert";
|
import { Alert, AlertDescription } from "@/components/ui/alert";
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import _ from "lodash";
|
import _ from "lodash";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
|
import { useQueryState } from "nuqs";
|
||||||
|
|
||||||
interface Option {
|
interface Option {
|
||||||
label: string;
|
label: string;
|
||||||
|
|
@ -62,6 +63,8 @@ export function ConfigField({
|
||||||
value: externalValue, // TODO: Rename to avoid conflict
|
value: externalValue, // TODO: Rename to avoid conflict
|
||||||
setValue: externalSetValue, // TODO: Rename to avoid conflict
|
setValue: externalSetValue, // TODO: Rename to avoid conflict
|
||||||
}: ConfigFieldProps) {
|
}: ConfigFieldProps) {
|
||||||
|
const [threadId] = useQueryState("threadId");
|
||||||
|
const configKey = threadId || DEFAULT_CONFIG_KEY;
|
||||||
const store = useConfigStore();
|
const store = useConfigStore();
|
||||||
const [jsonError, setJsonError] = useState<string | null>(null);
|
const [jsonError, setJsonError] = useState<string | null>(null);
|
||||||
|
|
||||||
|
|
@ -70,14 +73,14 @@ export function ConfigField({
|
||||||
|
|
||||||
const currentValue = isExternallyManaged
|
const currentValue = isExternallyManaged
|
||||||
? externalValue
|
? externalValue
|
||||||
: store.configs[id]?.[id];
|
: store.configs[configKey]?.[id];
|
||||||
|
|
||||||
const handleChange = (newValue: any) => {
|
const handleChange = (newValue: any) => {
|
||||||
setJsonError(null);
|
setJsonError(null);
|
||||||
if (isExternallyManaged && externalSetValue) {
|
if (isExternallyManaged && externalSetValue) {
|
||||||
externalSetValue(newValue);
|
externalSetValue(newValue);
|
||||||
} else {
|
} else {
|
||||||
store.updateConfig(id, newValue);
|
store.updateConfig(configKey, id, newValue);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -97,7 +100,7 @@ export function ConfigField({
|
||||||
if (isExternallyManaged && externalSetValue) {
|
if (isExternallyManaged && externalSetValue) {
|
||||||
externalSetValue(jsonString);
|
externalSetValue(jsonString);
|
||||||
} else {
|
} else {
|
||||||
store.updateConfig(id, jsonString);
|
store.updateConfig(configKey, id, jsonString);
|
||||||
}
|
}
|
||||||
setJsonError("Invalid JSON format");
|
setJsonError("Invalid JSON format");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ import { forwardRef, ForwardedRef, useState, useEffect } from "react";
|
||||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||||
import { ConfigField } from "@/components/configuration-sidebar/config-field";
|
import { ConfigField } from "@/components/configuration-sidebar/config-field";
|
||||||
import { ConfigSection } from "@/components/configuration-sidebar/config-section";
|
import { ConfigSection } from "@/components/configuration-sidebar/config-section";
|
||||||
import { useConfigStore } from "@/hooks/use-config-store";
|
import { useConfigStore, DEFAULT_CONFIG_KEY } from "@/hooks/use-config-store";
|
||||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||||
import { cn } from "@/lib/utils";
|
import { cn } from "@/lib/utils";
|
||||||
import { Skeleton } from "@/components/ui/skeleton";
|
import { Skeleton } from "@/components/ui/skeleton";
|
||||||
|
|
@ -12,11 +12,15 @@ import type { ConfigurableFieldUIMetadata } from "@open-swe/shared/configurable-
|
||||||
import { Button } from "@/components/ui/button";
|
import { Button } from "@/components/ui/button";
|
||||||
import { PanelRightOpen } from "lucide-react";
|
import { PanelRightOpen } from "lucide-react";
|
||||||
import { GraphConfigurationMetadata } from "@open-swe/shared/open-swe/types";
|
import { GraphConfigurationMetadata } from "@open-swe/shared/open-swe/types";
|
||||||
|
import { useQueryState } from "nuqs";
|
||||||
|
import { useStreamContext } from "@/providers/Stream";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Extract configuration metadata from the GraphConfiguration Zod schema
|
* Extract configuration metadata from the GraphConfiguration Zod schema
|
||||||
*/
|
*/
|
||||||
function extractConfigurationsFromSchema(): ConfigurableFieldUIMetadata[] {
|
function extractConfigurationsFromSchema(
|
||||||
|
configurable: Record<string, any>,
|
||||||
|
): ConfigurableFieldUIMetadata[] {
|
||||||
const configurations: ConfigurableFieldUIMetadata[] = [];
|
const configurations: ConfigurableFieldUIMetadata[] = [];
|
||||||
|
|
||||||
for (const [label, { x_open_swe_ui_config: metadata }] of Object.entries(
|
for (const [label, { x_open_swe_ui_config: metadata }] of Object.entries(
|
||||||
|
|
@ -28,7 +32,7 @@ function extractConfigurationsFromSchema(): ConfigurableFieldUIMetadata[] {
|
||||||
configurations.push({
|
configurations.push({
|
||||||
label,
|
label,
|
||||||
type: metadata.type,
|
type: metadata.type,
|
||||||
default: metadata.default,
|
default: configurable[label] || metadata.default,
|
||||||
description: metadata.description,
|
description: metadata.description,
|
||||||
placeholder: metadata.placeholder,
|
placeholder: metadata.placeholder,
|
||||||
options: metadata.options,
|
options: metadata.options,
|
||||||
|
|
@ -52,6 +56,9 @@ export const ConfigurationSidebar = forwardRef<
|
||||||
AIConfigPanelProps
|
AIConfigPanelProps
|
||||||
>(({ className, open, onClose }, ref: ForwardedRef<HTMLDivElement>) => {
|
>(({ className, open, onClose }, ref: ForwardedRef<HTMLDivElement>) => {
|
||||||
const { configs, updateConfig } = useConfigStore();
|
const { configs, updateConfig } = useConfigStore();
|
||||||
|
const stream = useStreamContext();
|
||||||
|
|
||||||
|
const [threadId] = useQueryState("threadId");
|
||||||
|
|
||||||
const [configurations, setConfigurations] = useState<
|
const [configurations, setConfigurations] = useState<
|
||||||
ConfigurableFieldUIMetadata[]
|
ConfigurableFieldUIMetadata[]
|
||||||
|
|
@ -60,17 +67,38 @@ export const ConfigurationSidebar = forwardRef<
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
const configKey = threadId || DEFAULT_CONFIG_KEY;
|
||||||
|
|
||||||
const actualConfigs = extractConfigurationsFromSchema();
|
if (threadId) {
|
||||||
actualConfigs.forEach((config) => {
|
stream.client.threads.get(threadId).then((t) => {
|
||||||
if (configs[config.label] === undefined && config.default !== undefined) {
|
if (
|
||||||
updateConfig(config.label, config.default);
|
!("config" in t) ||
|
||||||
}
|
!(t as any).config ||
|
||||||
});
|
!(t as any).config.configurable
|
||||||
|
) {
|
||||||
|
console.error("Thread does not have config key", t);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
setConfigurations(actualConfigs);
|
const actualConfigs = extractConfigurationsFromSchema(
|
||||||
|
(t.config as any).configurable,
|
||||||
|
);
|
||||||
|
actualConfigs.forEach((c) => {
|
||||||
|
// Always update the config store with either the default values, or the values from the thread.
|
||||||
|
updateConfig(configKey, c.label, c.default);
|
||||||
|
});
|
||||||
|
|
||||||
|
setConfigurations(actualConfigs);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const actualConfigs = extractConfigurationsFromSchema({});
|
||||||
|
actualConfigs.forEach((c) => {
|
||||||
|
updateConfig(configKey, c.label, c.default);
|
||||||
|
});
|
||||||
|
setConfigurations(actualConfigs);
|
||||||
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}, [configs, updateConfig]);
|
}, [threadId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ import { GitHubOAuthButton } from "../github/github-oauth-button";
|
||||||
import { useGitHubApp } from "@/hooks/useGitHubApp";
|
import { useGitHubApp } from "@/hooks/useGitHubApp";
|
||||||
import TaskList from "../task-list";
|
import TaskList from "../task-list";
|
||||||
import { ConfigurationSidebar } from "../configuration-sidebar";
|
import { ConfigurationSidebar } from "../configuration-sidebar";
|
||||||
import { useConfigStore } from "@/hooks/use-config-store";
|
import { DEFAULT_CONFIG_KEY, useConfigStore } from "@/hooks/use-config-store";
|
||||||
import { RepositoryBranchSelectors } from "../github/repo-branch-selectors";
|
import { RepositoryBranchSelectors } from "../github/repo-branch-selectors";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
import {
|
import {
|
||||||
|
|
@ -101,7 +101,7 @@ export function Thread() {
|
||||||
const [artifactContext, setArtifactContext] = useArtifactContext();
|
const [artifactContext, setArtifactContext] = useArtifactContext();
|
||||||
const [artifactOpen, closeArtifact] = useArtifactOpen();
|
const [artifactOpen, closeArtifact] = useArtifactOpen();
|
||||||
const { selectedRepository } = useGitHubApp();
|
const { selectedRepository } = useGitHubApp();
|
||||||
const { getConfigs } = useConfigStore();
|
const { getConfig } = useConfigStore();
|
||||||
const {
|
const {
|
||||||
taskPlan,
|
taskPlan,
|
||||||
handleTaskChange,
|
handleTaskChange,
|
||||||
|
|
@ -269,7 +269,7 @@ export function Thread() {
|
||||||
config: {
|
config: {
|
||||||
recursion_limit: 400,
|
recursion_limit: 400,
|
||||||
configurable: {
|
configurable: {
|
||||||
...getConfigs(),
|
...getConfig(threadId || DEFAULT_CONFIG_KEY),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
@ -294,7 +294,7 @@ export function Thread() {
|
||||||
config: {
|
config: {
|
||||||
recursion_limit: 400,
|
recursion_limit: 400,
|
||||||
configurable: {
|
configurable: {
|
||||||
...getConfigs(),
|
...getConfig(threadId || DEFAULT_CONFIG_KEY),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
metadata: {
|
metadata: {
|
||||||
|
|
|
||||||
|
|
@ -114,8 +114,6 @@ export function AssistantMessage({
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log("returning");
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="group mr-auto flex w-full max-w-3xl items-start gap-2">
|
<div className="group mr-auto flex w-full max-w-3xl items-start gap-2">
|
||||||
<div className="flex w-full flex-col gap-2">
|
<div className="flex w-full flex-col gap-2">
|
||||||
|
|
|
||||||
|
|
@ -2,21 +2,16 @@
|
||||||
|
|
||||||
import { create } from "zustand";
|
import { create } from "zustand";
|
||||||
import { persist } from "zustand/middleware";
|
import { persist } from "zustand/middleware";
|
||||||
import type { ConfigurableFieldUIMetadata } from "@open-swe/shared/configurable-metadata";
|
|
||||||
|
|
||||||
export const DEFAULT_CONFIG_KEY = "open_swe_default_config";
|
export const DEFAULT_CONFIG_KEY = "open_swe_default_config";
|
||||||
|
|
||||||
interface ConfigState {
|
interface ConfigState {
|
||||||
configs: Record<string, any>;
|
configs: Record<string, any>;
|
||||||
getConfig: (key: string) => Record<string, any>;
|
getConfig: (namespace: string) => Record<string, any>;
|
||||||
getConfigs: () => Record<string, any>;
|
getConfigs: () => Record<string, any>;
|
||||||
updateConfig: (key: string, value: any) => void;
|
updateConfig: (namespace: string, key: string, value: any) => void;
|
||||||
resetConfig: (key: string) => void;
|
resetConfig: (namespace: string) => void;
|
||||||
setDefaultConfig: (
|
resetStore: (namespace: string) => void;
|
||||||
key: string,
|
|
||||||
configurations: ConfigurableFieldUIMetadata[],
|
|
||||||
) => void;
|
|
||||||
resetStore: (key: string) => void;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useConfigStore = create<ConfigState>()(
|
export const useConfigStore = create<ConfigState>()(
|
||||||
|
|
@ -24,9 +19,9 @@ export const useConfigStore = create<ConfigState>()(
|
||||||
(set, get) => ({
|
(set, get) => ({
|
||||||
configs: {},
|
configs: {},
|
||||||
|
|
||||||
getConfig: (key: string) => {
|
getConfig: (namespace: string) => {
|
||||||
const state = get();
|
const state = get();
|
||||||
const baseConfig = state.configs[key];
|
const baseConfig = state.configs[namespace];
|
||||||
const configObj = {
|
const configObj = {
|
||||||
...baseConfig,
|
...baseConfig,
|
||||||
};
|
};
|
||||||
|
|
@ -36,33 +31,23 @@ export const useConfigStore = create<ConfigState>()(
|
||||||
|
|
||||||
getConfigs: () => {
|
getConfigs: () => {
|
||||||
const state = get();
|
const state = get();
|
||||||
const flatConfigs: Record<string, any> = {};
|
return state.configs;
|
||||||
Object.entries(state.configs).forEach(([key, configObj]) => {
|
|
||||||
if (
|
|
||||||
configObj &&
|
|
||||||
typeof configObj === "object" &&
|
|
||||||
configObj[key] !== undefined
|
|
||||||
) {
|
|
||||||
flatConfigs[key] = configObj[key];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return flatConfigs;
|
|
||||||
},
|
},
|
||||||
|
|
||||||
updateConfig: (key, value) =>
|
updateConfig: (namespace: string, key: string, value: any) =>
|
||||||
set((state) => ({
|
set((state) => ({
|
||||||
configs: {
|
configs: {
|
||||||
...state.configs,
|
...state.configs,
|
||||||
[key]: {
|
[namespace]: {
|
||||||
...(state.configs[key] || {}),
|
...(state.configs[namespace] || {}),
|
||||||
[key]: value,
|
[key]: value,
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
})),
|
})),
|
||||||
|
|
||||||
resetConfig: (key: string) => {
|
resetConfig: (namespace: string) => {
|
||||||
set((state) => {
|
set((state) => {
|
||||||
const config = state.configs[key];
|
const config = state.configs[namespace];
|
||||||
if (!config || !config.__defaultValues) {
|
if (!config || !config.__defaultValues) {
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
@ -70,33 +55,12 @@ export const useConfigStore = create<ConfigState>()(
|
||||||
return {
|
return {
|
||||||
configs: {
|
configs: {
|
||||||
...state.configs,
|
...state.configs,
|
||||||
[key]: defaultsToUse,
|
[namespace]: defaultsToUse,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
setDefaultConfig: (
|
|
||||||
key: string,
|
|
||||||
configurations: ConfigurableFieldUIMetadata[],
|
|
||||||
) => {
|
|
||||||
const defaultConfig: Record<string, any> = {};
|
|
||||||
configurations.forEach((config: ConfigurableFieldUIMetadata) => {
|
|
||||||
if (config.default !== undefined) {
|
|
||||||
defaultConfig[config.label] = config.default;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
defaultConfig.__defaultValues = { ...defaultConfig };
|
|
||||||
|
|
||||||
set((currentState) => ({
|
|
||||||
configs: {
|
|
||||||
...currentState.configs,
|
|
||||||
[key]: defaultConfig,
|
|
||||||
},
|
|
||||||
}));
|
|
||||||
},
|
|
||||||
|
|
||||||
resetStore: () => set({ configs: {} }),
|
resetStore: () => set({ configs: {} }),
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue