Skip to content

Commit

Permalink
Fix/playground (#366)
Browse files Browse the repository at this point in the history
* initial fix

* feat: upgrade ten runtime to 0.3 (#364)

* feat: version updateg

* feat: update build image

* fix: upgrade agora_rtc and azure_tts to fix build

* fix: fix build failure

* fix: fix property.jso

* fix: fix graph names

* fix: remove app: localhost

* fix: update frontend image

* feat: turn openai python extension to async extension

* fix: incompatible version

* fix: update agora_rtc and azure_tts to fix logs

* property.json update

* fix: fix build failure

* fix: fix bingsearch manifest

* feat: update to latest runtime

* feat: udpate build image to 0.2.1

* remove dummy token

---------

Co-authored-by: Jay Zhang <[email protected]>

* fix: whitespace-only properties cannot be editable (#361)

* more robust fix

* fix: environment example and property json to better support custom openai key & model

* feat: support playground settings persistance locally

* finalize

* playground image update

---------

Co-authored-by: Jay Zhang <[email protected]>
Co-authored-by: LIU Lin <[email protected]>
  • Loading branch information
3 people authored Oct 29, 2024
1 parent 4180cb0 commit b120e05
Show file tree
Hide file tree
Showing 10 changed files with 101 additions and 37 deletions.
2 changes: 1 addition & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ services:
networks:
- ten_agent_network
ten_agent_playground:
image: ghcr.io/ten-framework/ten_agent_playground:0.5.0-56-g0536dbb
image: ghcr.io/ten-framework/ten_agent_playground:0.5.0-66-g830dd72
container_name: ten_agent_playground
restart: always
ports:
Expand Down
1 change: 1 addition & 0 deletions playground/src/common/constant.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { IOptions, ColorItem, LanguageOptionItem, VoiceOptionItem, GraphOptionItem } from "@/types"
export const GITHUB_URL = "https://github.com/TEN-framework/TEN-Agent"
export const OPTIONS_KEY = "__options__"
export const OVERRIDEN_PROPERTIES_KEY = "__overriden__"
export const DEFAULT_OPTIONS: IOptions = {
channel: "",
userName: "",
Expand Down
22 changes: 19 additions & 3 deletions playground/src/common/hooks.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client"

import { IMicrophoneAudioTrack } from "agora-rtc-sdk-ng"
import { normalizeFrequencies } from "./utils"
import { deepMerge, normalizeFrequencies } from "./utils"
import { useState, useEffect, useMemo, useRef } from "react"
import type { AppDispatch, AppStore, RootState } from "../store"
import { useDispatch, useSelector, useStore } from "react-redux"
Expand Down Expand Up @@ -132,13 +132,29 @@ export const usePrevious = (value: any) => {
export const useGraphExtensions = () => {
const graphName = useAppSelector(state => state.global.graphName);
const nodes = useAppSelector(state => state.global.extensions);
const overridenProperties = useAppSelector(state => state.global.overridenProperties);
const [graphExtensions, setGraphExtensions] = useState<Record<string, any>>({});

useEffect(() => {
if (nodes && nodes[graphName]) {
setGraphExtensions(nodes[graphName]);
let extensions:Record<string, any> = {}
let extensionsByGraph = JSON.parse(JSON.stringify(nodes[graphName]));
let overriden = overridenProperties[graphName] || {};
for (const key of Object.keys(extensionsByGraph)) {
if (!overriden[key]) {
extensions[key] = extensionsByGraph[key];
continue;
}
extensions[key] = {
addon: extensionsByGraph[key].addon,
name: extensionsByGraph[key].name,
};
extensions[key].property = deepMerge(extensionsByGraph[key].property, overriden[key]);
}
setGraphExtensions(extensions);
}
}, [graphName, nodes]);

}, [graphName, nodes, overridenProperties]);

return graphExtensions;
};
17 changes: 15 additions & 2 deletions playground/src/common/storage.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { IOptions } from "@/types"
import { OPTIONS_KEY, DEFAULT_OPTIONS } from "./constant"
import { OPTIONS_KEY, DEFAULT_OPTIONS, OVERRIDEN_PROPERTIES_KEY } from "./constant"

export const getOptionsFromLocal = () => {
if (typeof window !== "undefined") {
Expand All @@ -11,11 +11,24 @@ export const getOptionsFromLocal = () => {
return DEFAULT_OPTIONS
}

export const getOverridenPropertiesFromLocal = () => {
if (typeof window !== "undefined") {
const data = localStorage.getItem(OVERRIDEN_PROPERTIES_KEY)
if (data) {
return JSON.parse(data)
}
}
return {}
}

export const setOptionsToLocal = (options: IOptions) => {
if (typeof window !== "undefined") {
localStorage.setItem(OPTIONS_KEY, JSON.stringify(options))
}
}


export const setOverridenPropertiesToLocal = (properties: Record<string, any>) => {
if (typeof window !== "undefined") {
localStorage.setItem(OVERRIDEN_PROPERTIES_KEY, JSON.stringify(properties))
}
}
10 changes: 10 additions & 0 deletions playground/src/common/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,14 @@ export const genUUID = () => {

export const isMobile = () => {
return /Mobile|iPhone|iPad|Android|Windows Phone/i.test(navigator.userAgent)
}

export const deepMerge = (target: Record<string, any>, source: Record<string, any>): Record<string, any> => {
for (const key of Object.keys(source)) {
if (source[key] instanceof Object && key in target) {
Object.assign(source[key], deepMerge(target[key], source[key]));
}
}
// Merge source into target
return { ...target, ...source };
}
8 changes: 5 additions & 3 deletions playground/src/components/authInitializer/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"use client"

import { ReactNode, useEffect } from "react"
import { useAppDispatch, getOptionsFromLocal, getRandomChannel, getRandomUserId } from "@/common"
import { setOptions, reset } from "@/store/reducers/global"
import { useAppDispatch, getOptionsFromLocal, getRandomChannel, getRandomUserId, getOverridenPropertiesFromLocal } from "@/common"
import { setOptions, reset, setOverridenProperties } from "@/store/reducers/global"

interface AuthInitializerProps {
children: ReactNode;
Expand All @@ -15,16 +15,18 @@ const AuthInitializer = (props: AuthInitializerProps) => {
useEffect(() => {
if (typeof window !== "undefined") {
const options = getOptionsFromLocal()
const overridenProperties = getOverridenPropertiesFromLocal()
if (options && options.channel) {
dispatch(reset())
dispatch(setOptions(options))
} else {
dispatch(reset())
dispatch(setOptions({
dispatch(setOptions({
channel: getRandomChannel(),
userId: getRandomUserId(),
}))
}
dispatch(setOverridenProperties(overridenProperties))
}
}, [dispatch])

Expand Down
21 changes: 14 additions & 7 deletions playground/src/platform/pc/chat/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import {
useGraphExtensions,
apiGetExtensionMetadata,
} from "@/common"
import { setExtensionMetadata, setGraphName, setGraphs, setLanguage, setExtensions } from "@/store/reducers/global"
import { setExtensionMetadata, setGraphName, setGraphs, setLanguage, setExtensions, setOverridenPropertiesByGraph, setOverridenProperties } from "@/store/reducers/global"
import { Button, Modal, Select, Tabs, TabsProps, } from 'antd';
import PdfSelect from "@/components/pdfSelect"

Expand All @@ -31,6 +31,7 @@ const Chat = () => {
const [modal2Open, setModal2Open] = useState(false)
const graphExtensions = useGraphExtensions()
const extensionMetadata = useAppSelector(state => state.global.extensionMetadata)
const overridenProperties = useAppSelector(state => state.global.overridenProperties)


// const chatItems = genRandomChatList(10)
Expand Down Expand Up @@ -93,9 +94,14 @@ const Chat = () => {
open={modal2Open}
onCancel={() => setModal2Open(false)}
footer={
<Button type="primary" onClick={() => setModal2Open(false)}>
Close
</Button>
<>
<Button type="default" onClick={() => { dispatch(setOverridenProperties({})) }}>
Clear Settings
</Button>
<Button type="primary" onClick={() => setModal2Open(false)}>
Close
</Button>
</>
}
>
<p>You can adjust extension properties here, the values will be overridden when the agent starts using "Connect." Note that this won't modify the property.json file.</p>
Expand All @@ -109,9 +115,10 @@ const Chat = () => {
initialData={node["property"] || {}}
metadata={metadata ? metadata.api.property : {}}
onUpdate={(data) => {
let nodesMap = JSON.parse(JSON.stringify(graphExtensions))
nodesMap[key]["property"] = data
dispatch(setExtensions({ graphName, nodesMap }))
// clone the overridenProperties
let nodesMap = JSON.parse(JSON.stringify(overridenProperties[graphName] || {}))
nodesMap[key] = data
dispatch(setOverridenPropertiesByGraph({ graphName, nodesMap }))
}}
></EditableTable>
}
Expand Down
29 changes: 19 additions & 10 deletions playground/src/platform/pc/chat/table/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,18 @@ const convertToType = (value: any, type: string) => {
};

const EditableTable: React.FC<EditableTableProps> = ({ initialData, onUpdate, metadata }) => {
const [dataSource, setDataSource] = useState<DataType[]>(
Object.entries(initialData).map(([key, value]) => ({ key, value }))
);
const [dataSource, setDataSource] = useState<DataType[]>([]);
const [editingKey, setEditingKey] = useState<string>('');
const [form] = Form.useForm();
const inputRef = useRef<InputRef>(null); // Ref to manage focus
const updatedValuesRef = useRef<Record<string, string | number | boolean | null>>({});

// Update dataSource whenever initialData changes
useEffect(() => {
setDataSource(
Object.entries(initialData).map(([key, value]) => ({ key, value }))
);
}, [initialData]);

// Function to check if the current row is being edited
const isEditing = (record: DataType) => record.key === editingKey;
Expand All @@ -65,16 +71,17 @@ const EditableTable: React.FC<EditableTableProps> = ({ initialData, onUpdate, me
setDataSource(newData);
setEditingKey('');

// Notify the parent component of the update
const updatedData = Object.fromEntries(newData.map(({ key, value }) => [key, value]));
onUpdate(updatedData);
// Store the updated value in the ref
updatedValuesRef.current[key] = updatedValue;

// Notify the parent component of only the updated value
onUpdate({ [key]: updatedValue });
}
} catch (errInfo) {
console.log('Validation Failed:', errInfo);
}
};


// Toggle the checkbox for boolean values directly in the table cell
const handleCheckboxChange = (key: string, checked: boolean) => {
const newData = [...dataSource];
Expand All @@ -83,9 +90,11 @@ const EditableTable: React.FC<EditableTableProps> = ({ initialData, onUpdate, me
newData[index].value = checked; // Update the boolean value
setDataSource(newData);

// Notify the parent component of the update
const updatedData = Object.fromEntries(newData.map(({ key, value }) => [key, value]));
onUpdate(updatedData);
// Store the updated value in the ref
updatedValuesRef.current[key] = checked;

// Notify the parent component of only the updated value
onUpdate({ [key]: checked });
}
};

Expand Down
13 changes: 4 additions & 9 deletions playground/src/platform/pc/description/index.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import { setAgentConnected } from "@/store/reducers/global"
import {
useAppDispatch, useAppSelector, apiPing, genUUID,
apiStartService, apiStopService,
useGraphExtensions
apiStartService, apiStopService
} from "@/common"
import { Select, Button, message, Upload } from "antd"
import { useEffect, useState, MouseEventHandler } from "react"
Expand All @@ -20,7 +19,7 @@ const Description = () => {
const voiceType = useAppSelector(state => state.global.voiceType)
const [loading, setLoading] = useState(false)
const graphName = useAppSelector(state => state.global.graphName)
const graphNodes = useGraphExtensions()
const overridenProperties = useAppSelector(state => state.global.overridenProperties)

useEffect(() => {
if (channel) {
Expand All @@ -47,18 +46,14 @@ const Description = () => {
message.success("Agent disconnected")
stopPing()
} else {
let properties: Record<string, any> = {}
Object.keys(graphNodes).forEach(extensionName => {
properties[extensionName] = {}
properties[extensionName] = graphNodes[extensionName].property
})
let properties: Record<string, any> = overridenProperties[graphName] || {}
const res = await apiStartService({
channel,
userId,
graphName,
language,
voiceType,
properties: properties
properties
})
const { code, msg } = res || {}
if (code != 0) {
Expand Down
15 changes: 13 additions & 2 deletions playground/src/store/reducers/global.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { IOptions, IChatItem, Language, VoiceType } from "@/types"
import { createSlice, PayloadAction } from "@reduxjs/toolkit"
import { DEFAULT_OPTIONS, COLOR_LIST, setOptionsToLocal, genRandomChatList } from "@/common"
import { DEFAULT_OPTIONS, COLOR_LIST, setOptionsToLocal, genRandomChatList, setOverridenPropertiesToLocal, deepMerge } from "@/common"

export interface InitialState {
options: IOptions
Expand All @@ -13,6 +13,7 @@ export interface InitialState {
graphName: string,
graphs: string[],
extensions: Record<string, any>,
overridenProperties: Record<string, any>,
extensionMetadata: Record<string, any>
}

Expand All @@ -28,6 +29,7 @@ const getInitialState = (): InitialState => {
graphName: "camera_va_openai_azure",
graphs: [],
extensions: {},
overridenProperties: {},
extensionMetadata: {},
}
}
Expand Down Expand Up @@ -100,6 +102,15 @@ export const globalSlice = createSlice({
let { graphName, nodesMap } = action.payload
state.extensions[graphName] = nodesMap
},
setOverridenProperties: (state, action: PayloadAction<Record<string, any>>) => {
state.overridenProperties = action.payload
setOverridenPropertiesToLocal(state.overridenProperties)
},
setOverridenPropertiesByGraph: (state, action: PayloadAction<Record<string, any>>) => {
let { graphName, nodesMap } = action.payload
state.overridenProperties[graphName] = deepMerge(state.overridenProperties[graphName] || {}, nodesMap)
setOverridenPropertiesToLocal(state.overridenProperties)
},
setExtensionMetadata: (state, action: PayloadAction<Record<string, any>>) => {
state.extensionMetadata = action.payload
},
Expand All @@ -115,7 +126,7 @@ export const globalSlice = createSlice({

export const { reset, setOptions,
setRoomConnected, setAgentConnected, setVoiceType,
addChatItem, setThemeColor, setLanguage, setGraphName, setGraphs, setExtensions, setExtensionMetadata } =
addChatItem, setThemeColor, setLanguage, setGraphName, setGraphs, setExtensions, setExtensionMetadata, setOverridenProperties, setOverridenPropertiesByGraph } =
globalSlice.actions

export default globalSlice.reducer

0 comments on commit b120e05

Please sign in to comment.