Merge pull request #55 from devchat-ai/usages

Usages
This commit is contained in:
Tim 2024-06-11 11:28:31 +08:00 committed by GitHub
commit a01e04ad81
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 175 additions and 39 deletions

111
src/util/APIUtil.ts Normal file
View File

@ -0,0 +1,111 @@
import axios from "axios";
import { v4 as uuidv4 } from 'uuid';
class APIUtil {
private static instance: APIUtil;
private baseUrl: string | undefined;
private accessKey: string | undefined;
private webappUrl: string | undefined;
private currentMessageId: string | undefined;
constructor() {
console.log("APIUtil ready");
}
public static getInstance(): APIUtil {
if (!APIUtil.instance) {
APIUtil.instance = new APIUtil();
}
return APIUtil.instance;
}
async fetchWebappUrl() {
try {
const res = await axios.get(
`${this.baseUrl}/addresses/webapp`,
{ headers: { 'Authorization': `Bearer ${this.accessKey}` }}
)
const urlOrPath = res?.data;
if (!urlOrPath) {
throw new Error("No webapp url found");
}
let href = "";
if (urlOrPath.startsWith("http://") || urlOrPath.startsWith("https://")) {
href = urlOrPath;
} else {
href = new URL(urlOrPath, this.baseUrl).href
}
if (href.endsWith('/')) {
href = href.slice(0, -1);
}
if (href.endsWith('/api')) {
href = href.slice(0, -4);
}
console.log('Webapp url: ', href)
return href;
} catch (err) {
console.error("Error fetch webapp url:", err);
return "https://app.devchat.ai";
}
}
config(baseUrl: string, accessKey: string) {
this.baseUrl = baseUrl;
this.accessKey = accessKey;
if (!this.webappUrl) {
this.fetchWebappUrl().then(url => {
this.webappUrl = url;
})
}
}
async createMessage(message: object) {
this.currentMessageId = `msg-${uuidv4()}`;
try {
const res = await axios.post(
`${this.webappUrl}/api/v1/messages`,
{...message, message_id: this.currentMessageId},
{ headers: {
Authorization: `Bearer ${this.accessKey}`,
'Content-Type': 'application/json',
}}
)
console.log("Message created: ", res?.data);
} catch(err) {
console.error(err);
}
}
async createEvent(event: object) {
try {
const res = await axios.post(
`${this.webappUrl}/api/v1/messages/${this.currentMessageId}/events`,
event,
{headers: {
Authorization: `Bearer ${this.accessKey}`,
'Content-Type': 'application/json',
}}
)
console.log("Event created: ", res?.data);
} catch(err) {
console.error(err);
}
}
async getBalance() {
try {
if (!this.webappUrl) this.webappUrl = await this.fetchWebappUrl();
const res = await axios.get(
`${this.webappUrl}/api/v1/users/profile`,
{headers: { Authorization: `Bearer ${this.accessKey}` }}
)
return res?.data?.organization
} catch(err) {
console.error(err);
return null;
}
}
}
export default APIUtil.getInstance();

View File

@ -329,6 +329,9 @@ class IdeaBridge {
case "updateSetting/response":
this.resviceUpdateSetting(res);
break;
case "codeDiffApply/response":
this.resviceCodeDiffApply(res);
break;
case "sendUserMessage/response":
this.resviceSendUserMessage(res);
break;
@ -377,6 +380,10 @@ class IdeaBridge {
this.executeHandlers("updateSetting", res.payload);
}
resviceCodeDiffApply(res) {
this.executeHandlers("codeDiffApply", res.payload);
}
resviceSendUserMessage(res) {
this.executeHandlers("chatWithDevChat", {
command: "chatWithDevChat",

View File

@ -8,6 +8,7 @@ import { useMst } from "./stores/RootStore";
import MessageUtil from "@/util/MessageUtil";
import "./App.css";
import "./i18n";
import APIUtil from "@/util/APIUtil";
export default function App() {
const [ready, setReady] = useState(false);
@ -28,6 +29,7 @@ export default function App() {
MessageUtil.registerHandler("readConfig", (data: { value: any }) => {
console.log("readConfig registerHandler: ", data);
config.setConfig(data.value);
APIUtil.config(config.getAPIBase(), config.getUserKey())
config.refreshModelList();
setReady(true);
});

View File

@ -1,5 +1,4 @@
import React, { useEffect, useState } from "react";
import axios from "axios";
import messageUtil from "@/util/MessageUtil";
import { IconWallet } from "@tabler/icons-react";
import {
@ -11,6 +10,7 @@ import {
} from "@mantine/core";
import { Trans } from "react-i18next";
import { useMst } from "@/views/stores/RootStore";
import APIUtil from "@/util/APIUtil";
const currencyMap = {
USD: "$",
@ -28,15 +28,9 @@ function formatCurrency(balance: number | null, currency: string) {
return `${currencyMap[currency] || currency}${balance}`;
}
const envMap = {
dev: {
requestUrl: "https://apptest.devchat.ai",
link: "https://webtest.devchat.ai",
},
prod: {
requestUrl: "https://app.devchat.ai",
link: "https://web.devchat.ai",
},
const links = {
dev: "https://webtest.devchat.ai",
prod: "https://web.devchat.ai",
};
export default function WechatTip() {
@ -50,23 +44,13 @@ export default function WechatTip() {
const platform = process.env.platform;
const getBalance = () => {
if (!envMap[env].requestUrl || !accessKey) {
return;
}
setLoading(true);
axios
.get(`${envMap[env].requestUrl}/api/v1/users/profile`, {
headers: { Authorization: `Bearer ${accessKey}` },
})
.then((res) => {
if (res?.data?.organization?.balance) {
setBalance(formatBalance(res?.data?.organization?.balance));
setCurrency(res?.data?.organization?.currency);
}
})
.finally(() => {
setLoading(false);
});
APIUtil.getBalance().then(org => {
setLoading(true);
setBalance(formatBalance(org?.balance));
setCurrency(org?.currency);
}).finally(() => {
setLoading(false);
})
};
useEffect(() => {
@ -93,7 +77,7 @@ export default function WechatTip() {
e.stopPropagation();
messageUtil.sendMessage({
command: "openLink",
url: envMap[env].link,
url: links[env],
});
};
@ -139,7 +123,7 @@ export default function WechatTip() {
web.devchat.ai{" "}
</Text>
) : (
<a href={envMap[env].link} target="_blank">
<a href={links[env]} target="_blank">
web.devchat.ai{" "}
</a>
)}

View File

@ -1,9 +1,10 @@
import { Tooltip, ActionIcon, CopyButton, Flex } from "@mantine/core";
import { IconCheck, IconGitCommit, IconFileDiff, IconColumnInsertRight, IconReplace, IconCopy,IconFile } from "@tabler/icons-react";
import React, { useState } from "react";
import { useMst } from "@/views/stores/RootStore";
import messageUtil from '@/util/MessageUtil';
import language from "react-syntax-highlighter/dist/esm/languages/hljs/1c";
import APIUtil from "@/util/APIUtil";
const IconButton = ({ label, color = 'gray', onClick, children }) => (
<Tooltip sx={{ padding: '3px', fontSize: 'var(--vscode-editor-font-size)' }} label={label} withArrow position="left" color="gray">
@ -31,10 +32,14 @@ const CommitButton = ({ code }) => {
};
const CodeCopyButton = ({ code }) => {
const {config} = useMst();
return (
<CopyButton value={code} timeout={2000}>
{({ copied, copy }) => (
<IconButton label={copied ? 'Copied' : 'Copy'} color={copied ? 'teal' : 'gray'} onClick={copy}>
<IconButton label={copied ? 'Copied' : 'Copy'} color={copied ? 'teal' : 'gray'} onClick={() => {
copy();
APIUtil.createEvent({name: 'copy', value: 'copy'})
}}>
{copied ? <IconCheck size="1rem" /> : <IconCopy size="1rem" />}
</IconButton>
)}
@ -43,11 +48,14 @@ const CodeCopyButton = ({ code }) => {
};
const DiffButton = ({ code }) => {
const {config} = useMst();
const handleClick = () => {
const e = 'show_diff';
messageUtil.sendMessage({
command: 'show_diff',
command: e,
content: code
});
APIUtil.createEvent({name: e, value: e})
};
return (
<IconButton label='View Diff' onClick={handleClick}>
@ -57,11 +65,14 @@ const DiffButton = ({ code }) => {
};
const CodeApplyButton = ({ code }) => {
const {config} = useMst();
const handleClick = () => {
const e = 'code_apply';
messageUtil.sendMessage({
command: 'code_apply',
command: e,
content: code
});
APIUtil.createEvent({name: e, value: e})
};
return (
<IconButton label='Insert Code' onClick={handleClick}>
@ -71,11 +82,14 @@ const CodeApplyButton = ({ code }) => {
};
const FileApplyButton = ({ code }) => {
const {config} = useMst();
const handleClick = () => {
const e = 'code_file_apply';
messageUtil.sendMessage({
command: 'code_file_apply',
command: e,
content: code
});
APIUtil.createEvent({name: e, value: e})
};
return (
<IconButton label='Replace File' onClick={handleClick}>
@ -86,12 +100,15 @@ const FileApplyButton = ({ code }) => {
// Add a new button to create new file
const NewFileButton = ({ language,code }) => {
const {config} = useMst();
const handleClick = () => {
const e = 'code_new_file';
messageUtil.sendMessage({
command: 'code_new_file',
command: e,
language: language,
content: code
});
APIUtil.createEvent({name: e, value: e})
};
return (
<IconButton label='Create New File' onClick={handleClick}>

View File

@ -17,6 +17,7 @@ import { useSetState } from "@mantine/hooks";
import { useTranslation } from "react-i18next";
import { useRouter } from "@/views/router";
import remarkGfm from "remark-gfm";
import APIUtil from "@/util/APIUtil";
(typeof global !== "undefined" ? global : window).Prism = Prism;
require("prismjs/components/prism-java");
@ -70,7 +71,7 @@ function parseMetaData(string) {
const MessageMarkdown = observer((props: MessageMarkdownProps) => {
const { children, activeStep = false, messageDone } = props;
const { chat } = useMst();
const { config, chat } = useMst();
const router = useRouter();
const [steps, setSteps] = useState<Step[]>([]);
const tree = fromMarkdown(children);
@ -107,6 +108,13 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
});
};
const handleCodeCopy = (event) => {
const selection = window.getSelection()?.toString();
console.log("Copied: ", selection);
const e = 'manual_copy';
APIUtil.createEvent({name: e, value: selection})
}
useEffect(() => {
let previousNode: any = null;
let chatmarkCount = 0;
@ -328,6 +336,7 @@ const MessageMarkdown = observer((props: MessageMarkdownProps) => {
whiteSpace: "pre",
...props.style,
}}
onCopy={handleCodeCopy}
{...props}
>
{tokens.map((line, i) => (

View File

@ -5,6 +5,7 @@ import yaml from "js-yaml";
import { RootInstance } from "./RootStore";
import { useTranslation } from "react-i18next";
import i18next from "i18next";
import APIUtil from "@/util/APIUtil";
interface Context {
content: string;
@ -171,7 +172,12 @@ export const ChatStore = types
self.hasDone = false;
self.errorMessage = "";
self.currentMessage = "";
const chatModel = getParent<RootInstance>(self).config.getDefaultModel();
const config = getParent<RootInstance>(self).config
const chatModel = config.getDefaultModel();
messageUtil.registerHandler("codeDiffApply", (_: any) => {
const e = 'code_diff_apply'
APIUtil.createEvent({name: e, value: e})
})
messageUtil.sendMessage({
command: "sendMessage",
text: text,
@ -179,6 +185,7 @@ export const ChatStore = types
parent_hash: lastNonEmptyHash(),
model: chatModel,
});
APIUtil.createMessage({content: text, model: chatModel});
};
const helpMessage = (originalMessage = false) => {
@ -390,7 +397,7 @@ Thinking...
self.messages.push(...messages);
},
updateLastMessage: (message: string) => {
console.log("message: ", message);
// console.log("message: ", message);
if (self.messages.length > 0) {
self.messages[self.messages.length - 1].message = message;
// if (message === "") {

View File

@ -2,7 +2,6 @@ import MessageUtil from "@/util/MessageUtil";
import { types, Instance, flow } from "mobx-state-tree";
import modelsTemplate from "@/models";
import cloneDeep from "lodash.clonedeep";
import { set } from "mobx";
import axios from "axios";
const defaultAPIBase = [