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

feat: Update Memeooor agent #629

Merged
merged 14 commits into from
Dec 27, 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
7 changes: 1 addition & 6 deletions frontend/.eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,12 +29,7 @@
"simple-import-sort/imports": "error",
"simple-import-sort/exports": "error",
"unused-imports/no-unused-imports": "error",
"no-unused-vars": [
"warn",
{
"argsIgnorePattern": "^_"
}
],
"no-unused-vars": "off",
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

any reason to do it? 😅

"no-console": [
"error",
{
Expand Down
6 changes: 5 additions & 1 deletion frontend/client/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ export type ChainData = {

export type MiddlewareServiceResponse = {
service_config_id: string; // TODO: update with uuid once middleware integrated
version: number;
name: string;
hash: string;
hash_history: {
Expand All @@ -54,7 +55,10 @@ export type MiddlewareServiceResponse = {
home_chain: MiddlewareChain;
keys: ServiceKeys[];
service_path?: string;
version: string;
description: string;
env_variables: {
[key: string]: EnvVariableAttributes;
};
chain_configs: {
[middlewareChain: string]: {
ledger_config: LedgerConfig;
Expand Down
15 changes: 13 additions & 2 deletions frontend/components/Card/CardTitle.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,19 @@
import { ArrowLeftOutlined } from '@ant-design/icons';
import { Flex, Typography } from 'antd';
import Button from 'antd/es/button';
import { isFunction } from 'lodash';
import { ReactNode } from 'react';

export const CardTitle = ({ title }: { title: string | ReactNode }) => (
<Flex justify="space-between" align="center">
type CardTitleProps = {
title: string | ReactNode;
backButtonCallback?: () => void;
};

export const CardTitle = ({ title, backButtonCallback }: CardTitleProps) => (
<Flex justify="start" align="center" gap={12}>
{isFunction(backButtonCallback) && (
<Button onClick={backButtonCallback} icon={<ArrowLeftOutlined />} />
)}
<Typography.Title className="m-0" level={4}>
{title}
</Typography.Title>
Expand Down
19 changes: 19 additions & 0 deletions frontend/components/MainPage/sections/SwitchAgentSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import { ControlOutlined } from '@ant-design/icons';
import { Button, Flex, Popover, Typography } from 'antd';
import Image from 'next/image';
import { useMemo } from 'react';

import { CardSection } from '@/components/styled/CardSection';
import { AgentType } from '@/enums/Agent';
import { Pages } from '@/enums/Pages';
import { usePageState } from '@/hooks/usePageState';
import { useService } from '@/hooks/useService';
Expand All @@ -11,6 +13,22 @@ import { useStakingContractContext } from '@/hooks/useStakingContractDetails';

const { Text } = Typography;

const UpdateTemplate = () => {
const { goto } = usePageState();
const { selectedAgentType } = useServices();

const handleClick = (e: React.MouseEvent) => {
e.stopPropagation();
goto(Pages.UpdateAgentTemplate);
};

if (selectedAgentType === AgentType.Memeooorr) {
return <ControlOutlined onClick={handleClick} />;
}

return null;
};

export const SwitchAgentSection = () => {
const { goto } = usePageState();
const {
Expand Down Expand Up @@ -51,6 +69,7 @@ export const SwitchAgentSection = () => {
alt={selectedAgentConfig.displayName}
/>
<Text>{selectedAgentConfig.displayName}</Text>
<UpdateTemplate />
</Flex>

{isSwitchAgentEnabled ? (
Expand Down
74 changes: 25 additions & 49 deletions frontend/components/SetupPage/SetupYourAgent/SetupYourAgent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,39 +17,32 @@ import { CustomAlert } from '@/components/Alert';
import { CardFlex } from '@/components/styled/CardFlex';
import { SERVICE_TEMPLATES } from '@/constants/serviceTemplates';
import { SetupScreen } from '@/enums/SetupScreen';
import { useElectronApi } from '@/hooks/useElectronApi';
import { useServices } from '@/hooks/useServices';
import { useSetup } from '@/hooks/useSetup';
import { useStakingProgram } from '@/hooks/useStakingProgram';
import { LOCAL_FORM_THEME } from '@/theme';

import { SetupCreateHeader } from '../Create/SetupCreateHeader';
import {
onDummyServiceCreation,
validateGeminiApiKey,
validateTwitterCredentials,
} from './validation';
import { useMemeFormValidate } from '../hooks/useMemeFormValidate';
import { onDummyServiceCreation } from './validation';

const { Title, Text } = Typography;

// TODO: consolidate theme into mainTheme
const LOCAL_THEME = { components: { Input: { fontSize: 16 } } };

type FieldValues = {
personaDescription: string;
geminiApiKey: string;
xEmail: string;
xUsername: string;
xPassword: string;
};
type ValidationStatus = 'valid' | 'invalid' | 'unknown';

const requiredRules = [{ required: true, message: 'Field is required' }];
const validateMessages = {
export const requiredRules = [{ required: true, message: 'Field is required' }];
export const validateMessages = {
required: 'Field is required',
types: { email: 'Enter a valid email' },
};

const XAccountCredentials = () => (
export const XAccountCredentials = () => (
<Flex vertical>
<Divider style={{ margin: '16px 0' }} />
<Title level={5} className="mt-0">
Expand Down Expand Up @@ -80,7 +73,7 @@ const XAccountCredentials = () => (
</Flex>
);

const InvalidGeminiApiCredentials = () => (
export const InvalidGeminiApiCredentials = () => (
<CustomAlert
type="error"
showIcon
Expand All @@ -89,7 +82,7 @@ const InvalidGeminiApiCredentials = () => (
/>
);

const InvalidXCredentials = () => (
export const InvalidXCredentials = () => (
<CustomAlert
type="error"
showIcon
Expand All @@ -101,19 +94,21 @@ const InvalidXCredentials = () => (
type SetupYourAgentFormProps = { serviceTemplate: ServiceTemplate };
// Agent setup form
const SetupYourAgentForm = ({ serviceTemplate }: SetupYourAgentFormProps) => {
const electronApi = useElectronApi();
const { goto } = useSetup();
const { defaultStakingProgramId } = useStakingProgram();

const [form] = Form.useForm<FieldValues>();
const [isSubmitting, setIsSubmitting] = useState(false);
const [submitButtonText, setSubmitButtonText] = useState('Continue');
const [geminiApiKeyValidationStatus, setGeminiApiKeyValidationStatus] =
useState<ValidationStatus>('unknown');
const [

const {
submitButtonText,
setSubmitButtonText,
geminiApiKeyValidationStatus,
setGeminiApiKeyValidationStatus,
twitterCredentialsValidationStatus,
setTwitterCredentialsValidationStatus,
] = useState<ValidationStatus>('unknown');
handleValidate,
} = useMemeFormValidate();

const onFinish = useCallback(
async (values: Record<keyof FieldValues, string>) => {
Expand All @@ -122,34 +117,9 @@ const SetupYourAgentForm = ({ serviceTemplate }: SetupYourAgentFormProps) => {
try {
setIsSubmitting(true);

// validate the gemini API
setSubmitButtonText('Validating Gemini API key...');
const isGeminiApiValid = await validateGeminiApiKey(
values.geminiApiKey,
);
setGeminiApiKeyValidationStatus(isGeminiApiValid ? 'valid' : 'invalid');
if (!isGeminiApiValid) return;

// validate the twitter credentials
setSubmitButtonText('Validating Twitter credentials...');
const { isValid: isTwitterCredentialsValid, cookies } =
electronApi?.validateTwitterLogin
? await validateTwitterCredentials(
values.xEmail,
values.xUsername,
values.xPassword,
electronApi.validateTwitterLogin,
)
: { isValid: false, cookies: undefined };
setTwitterCredentialsValidationStatus(
isTwitterCredentialsValid ? 'valid' : 'invalid',
);
if (!isTwitterCredentialsValid) return;
const cookies = await handleValidate(values);
if (!cookies) return;

// wait for agent setup to complete
setSubmitButtonText('Setting up agent...');

const overriddenServiceConfig: ServiceTemplate = {
...serviceTemplate,
description: `Memeooorr @${values.xUsername}`,
Expand Down Expand Up @@ -199,7 +169,13 @@ const SetupYourAgentForm = ({ serviceTemplate }: SetupYourAgentFormProps) => {
setSubmitButtonText('Continue');
}
},
[electronApi, defaultStakingProgramId, serviceTemplate, goto],
[
defaultStakingProgramId,
handleValidate,
serviceTemplate,
goto,
setSubmitButtonText,
],
);

// Clean up
Expand Down Expand Up @@ -336,7 +312,7 @@ export const SetupYourAgent = () => {
}

return (
<ConfigProvider theme={LOCAL_THEME}>
<ConfigProvider theme={LOCAL_FORM_THEME}>
<CardFlex gap={10} styles={{ body: { padding: '12px 24px' } }}>
<SetupCreateHeader prev={SetupScreen.AgentSelection} />
<Title level={3}>Set up your agent</Title>
Expand Down
87 changes: 87 additions & 0 deletions frontend/components/SetupPage/hooks/useMemeFormValidate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
import { useCallback, useState } from 'react';

import { useElectronApi } from '@/hooks/useElectronApi';

import {
validateGeminiApiKey,
validateTwitterCredentials,
} from '../SetupYourAgent/validation';

type ValidationStatus = 'valid' | 'invalid' | 'unknown';

type FieldValues = {
personaDescription: string;
geminiApiKey: string;
xEmail: string;
xUsername: string;
xPassword: string;
};

export const useMemeFormValidate = () => {
const electronApi = useElectronApi();

const [isValidating, setIsValidating] = useState(false);
const [submitButtonText, setSubmitButtonText] = useState('Continue');
const [geminiApiKeyValidationStatus, setGeminiApiKeyValidationStatus] =
useState<ValidationStatus>('unknown');
const [
twitterCredentialsValidationStatus,
setTwitterCredentialsValidationStatus,
] = useState<ValidationStatus>('unknown');

const handleValidate = useCallback(
async (values: Record<keyof FieldValues, string>) => {
setIsValidating(true);

setGeminiApiKeyValidationStatus('unknown');
setTwitterCredentialsValidationStatus('unknown');
setSubmitButtonText('Validating Gemini API key...');

try {
const isGeminiApiValid = await validateGeminiApiKey(
values.geminiApiKey,
);
setGeminiApiKeyValidationStatus(isGeminiApiValid ? 'valid' : 'invalid');
if (!isGeminiApiValid) return;

// validate the twitter credentials
setSubmitButtonText('Validating Twitter credentials...');
const { isValid: isTwitterCredentialsValid, cookies } =
electronApi?.validateTwitterLogin
? await validateTwitterCredentials(
values.xEmail,
values.xUsername,
values.xPassword,
electronApi.validateTwitterLogin,
)
: { isValid: false, cookies: undefined };
setTwitterCredentialsValidationStatus(
isTwitterCredentialsValid ? 'valid' : 'invalid',
);
if (!isTwitterCredentialsValid) return;
if (!cookies) return;

// wait for agent setup to complete
setSubmitButtonText('Setting up agent...');

return cookies;
} catch (error) {
console.error('Error validating meme form:', error);
} finally {
setIsValidating(false);
}
},
[electronApi.validateTwitterLogin],
);

return {
isValidating,
submitButtonText,
setSubmitButtonText,
geminiApiKeyValidationStatus,
setGeminiApiKeyValidationStatus,
twitterCredentialsValidationStatus,
setTwitterCredentialsValidationStatus,
handleValidate,
};
};
Loading
Loading