Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix(web&server):fix web copilot input issue,fix error while database not existing #14

Merged
merged 5 commits into from
Aug 14, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,28 @@ export class DedicatedDatabaseTaskService {
const manifest = await this.dbService.getDeployManifest(region, user, appid)

if (!manifest) {
throw new Error(`stop dedicated database ${appid} manifest not found`)
await this.db
.collection<DedicatedDatabase>('DedicatedDatabase')
.updateOne(
{
appid: data.appid,
phase: DedicatedDatabasePhase.Stopping,
},

{
$set: {
phase: DedicatedDatabasePhase.Stopped,
lockedAt: TASK_LOCK_INIT_TIME,
updatedAt: new Date(),
},
},
)

this.logger.debug(
`update dedicated database ${appid} state to stopped,note: ddb manifest not found`,
)

return
}

const stopped =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -146,9 +146,11 @@ export class DedicatedDatabaseService {
const requestMemory =
limitMemory * (region.bundleConf?.memoryRequestLimitRatio || 0.5)

const label = appid
const template = region.deployManifest.database
const tmpl = _.template(template)
const manifest = tmpl({
label,
name,
limitCPU,
limitMemory,
Expand All @@ -167,6 +169,12 @@ export class DedicatedDatabaseService {
appid: string,
): Promise<boolean> {
const ddbDeployManifest = await this.getDeployManifest(region, user, appid)

if (!ddbDeployManifest) {
this.logger.debug(`restart ddb, deploy manifest not found for ${appid}`)
return true
}

const replicas = Number(ddbDeployManifest.spec.componentSpecs[0].replicas)

const limitCPU = extractNumber(
Expand Down
1 change: 1 addition & 0 deletions server/src/initializer/deploy-manifest/database.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ metadata:
clusterdefinition.kubeblocks.io/name: mongodb
clusterversion.kubeblocks.io/name: mongodb-5.0
sealos-db-provider-cr: <%- name %>
sealaf-app: <%- label %>
annotations: {}
name: <%- name %>
spec:
Expand Down
20 changes: 15 additions & 5 deletions server/src/instance/instance.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export class InstanceService {
deployment.spec = await this.makeDeploymentSpec(
app,
deployment.spec.template.metadata.labels,
this.getRuntimeLabel(appid),
deployment.spec.template.metadata.labels,
)
const appsV1Api = this.cluster.makeAppsV1Api()
const deploymentResult = await appsV1Api.replaceNamespacedDeployment(
Expand Down Expand Up @@ -242,11 +242,19 @@ export class InstanceService {
// db connection uri
let dbConnectionUri: string
const dedicatedDatabase = await this.dedicatedDatabaseService.findOne(appid)

if (dedicatedDatabase) {
dbConnectionUri = await this.dedicatedDatabaseService.getConnectionUri(
user,
dedicatedDatabase,
)
try {
dbConnectionUri = await this.dedicatedDatabaseService.getConnectionUri(
user,
dedicatedDatabase,
)
} catch (e) {
dbConnectionUri = ''
this.logger.debug(
`get db connection uri for ${appid} failed: ${e}, maybe ddb cluster manifest have been deleted`,
)
}
}

const NODE_MODULES_PUSH_URL =
Expand Down Expand Up @@ -582,10 +590,12 @@ export class InstanceService {

private getRuntimeLabel(appid: string) {
const SEALOS = 'cloud.sealos.io/app-deploy-manager'
const SEALAF_APP = 'sealaf-app'
const labels: Record<string, string> = {
[LABEL_KEY_APP_ID]: appid,
[SEALOS]: this.getAppDeployName(appid),
app: this.getAppDeployName(appid),
[SEALAF_APP]: appid,
}

return labels
Expand Down
157 changes: 108 additions & 49 deletions web/src/pages/app/functions/mods/AIChatPanel/index.tsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import { useRef, useState } from "react";
import { KeyboardEvent, useCallback, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { FaRegStopCircle } from "react-icons/fa";
import { Avatar, Button, HStack, Input } from "@chakra-ui/react";
import { Avatar, Box, Button, Textarea, VStack } from "@chakra-ui/react";
import { useMutation } from "@tanstack/react-query";
import axios from "axios";
import { debounce } from "lodash";
import { v4 as uuidv4 } from "uuid";

import { LafAILogoIcon } from "@/components/CommonIcon";
Expand All @@ -19,7 +20,7 @@ export default function AIChatPanel() {
let source = CancelToken.source();

const contentDomRef = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null);
const textareaRef = useRef<HTMLTextAreaElement>(null);

const [prompt, setPrompt] = useState("");

Expand All @@ -31,6 +32,9 @@ export default function AIChatPanel() {
},
]);

const [contentHeight, setContentHeight] = useState("calc(100% - 120px)");
const [isComposing, setIsComposing] = useState(false);

async function handleSubmit() {
if (generateCode.isLoading) {
generateCode.reset();
Expand Down Expand Up @@ -69,14 +73,15 @@ export default function AIChatPanel() {
});
}, 100);
setPrompt("");
resetHeight();
await generateCode.mutateAsync({
value: prompt,
});
}
}

const { data: generateCodeRes, ...generateCode } = useMutation((params: any) => {
inputRef.current?.focus();
textareaRef.current?.focus();
return axios({
url: siteSettings.ai_pilot_url?.value,
method: "POST",
Expand Down Expand Up @@ -104,64 +109,118 @@ export default function AIChatPanel() {
});
});

const adjustTextareaHeight = useCallback(
debounce(() => {
const textarea = textareaRef.current;
if (textarea) {
textarea.style.height = "auto";
const scrollHeight = textarea.scrollHeight;
const newHeight = scrollHeight <= 120 ? 120 : Math.min(scrollHeight, 280);
textarea.style.height = `${newHeight}px`;
setContentHeight(`calc(100% - ${newHeight}px)`);
}
}, 100),
[],
);

useEffect(() => {
return () => {
adjustTextareaHeight.cancel();
};
}, [adjustTextareaHeight]);

const handleKeyDown = (e: KeyboardEvent<HTMLTextAreaElement>) => {
if (e.key === "Enter" && !e.shiftKey && !isComposing) {
e.preventDefault();
handleSubmit();
}
};

const resetHeight = useCallback(() => {
const textarea = textareaRef.current;
if (textarea) {
textarea.style.height = "120px";
setContentHeight("calc(100% - 120px)");
}
}, []);

const handleChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
adjustTextareaHeight();
setPrompt(e.target.value);
};

return (
<div className="flex h-full flex-col overflow-hidden px-2">
<div className="flex-1 overflow-scroll pr-2" ref={contentDomRef}>
<VStack height="100%">
<Box
ref={contentDomRef}
flex={1}
width="100%"
overflow="auto"
height={contentHeight}
transition="height 0.3s ease-in-out"
padding={[1, 1, 1, 2]}
>
<ul>
{aiChatHistory.map((item, index) => {
return (
<li className="mt-4 w-full overflow-hidden" key={item._id}>
<div className="flex">
<div className="mr-2 w-[24px]">
{item.isAi ? (
<LafAILogoIcon className="mr-2" />
) : (
<Avatar className="mr-2" size="xs" />
)}
</div>

{aiChatHistory.map((item, index) => (
<li key={item._id} className="mt-4 w-full overflow-hidden">
<div className="flex">
<div className="mr-2 w-[24px]">
{item.isAi ? (
<div className="mt-1 w-full flex-1 overflow-auto">
<Markdown
source={item.text}
formatLink
isChatting={index === aiChatHistory.length - 1 && generateCode.isLoading}
/>
</div>
<LafAILogoIcon className="mr-2" />
) : (
<div className="mt-1 flex-1 break-all">{item.text}</div>
<Avatar className="mr-2" size="xs" />
)}
</div>
</li>
);
})}
<div className="mt-1 flex-1 overflow-auto">
{item.isAi ? (
<Markdown
source={item.text}
formatLink
isChatting={index === aiChatHistory.length - 1 && generateCode.isLoading}
/>
) : (
<div className="break-all">{item.text}</div>
)}
</div>
</div>
</li>
))}
</ul>
</div>

<HStack className="h-[60px]">
<Input
h={"34px"}
placeholder={String(t("SendMessagePlaceHolder"))}
</Box>
<Box width="100%" position="relative" minHeight="120px" maxHeight="280px">
<Textarea
value={prompt}
ref={inputRef}
onChange={(e) => {
setPrompt(e.target.value);
}}
onKeyPress={(event: any) => {
if (event.key === "Enter") {
handleSubmit();
}
onKeyDown={handleKeyDown}
onChange={handleChange}
onCompositionStart={() => setIsComposing(true)}
onCompositionEnd={() => setIsComposing(false)}
minHeight="120px"
maxHeight="280px"
overflow="auto"
ref={textareaRef}
resize="none"
transition="height 0.3s ease-in-out"
sx={{
"&::-webkit-scrollbar": {
width: "8px",
},
"&::-webkit-scrollbar-thumb": {
backgroundColor: "gray.300",
borderRadius: "4px",
},
}}
/>
<Button
rounded={"md"}
onClick={async () => {
handleSubmit();
}}
rounded="md"
onClick={handleSubmit}
position="absolute"
bottom="4"
right="4"
zIndex={1}
>
{generateCode.isLoading ? <FaRegStopCircle fontSize={22} /> : t("Send")}
</Button>
</HStack>
</div>
</Box>
</VStack>
);
}
Loading