Skip to content

Commit

Permalink
feat: Basic agents private beta frontend
Browse files Browse the repository at this point in the history
  • Loading branch information
RezaRahemtola committed Nov 5, 2024
1 parent cdad239 commit 69903f2
Show file tree
Hide file tree
Showing 13 changed files with 260 additions and 36 deletions.
12 changes: 12 additions & 0 deletions src/apis/agents/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,18 @@ export const GetAgentResponseSchema = {
title: 'GetAgentResponse'
} as const;

export const GetAgentSecretMessageSchema = {
properties: {
message: {
type: 'string',
title: 'Message'
}
},
type: 'object',
required: ['message'],
title: 'GetAgentSecretMessage'
} as const;

export const GetAgentSecretResponseSchema = {
properties: {
secret: {
Expand Down
13 changes: 12 additions & 1 deletion src/apis/agents/services.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createClient, createConfig, type Options, formDataBodySerializer } from '@hey-api/client-axios';
import type { SetupAgentPostData, SetupAgentPostError, SetupAgentPostResponse, DeleteAgentDeleteData, DeleteAgentDeleteError, DeleteAgentDeleteResponse, GetAgentPublicInfoAgentAgentIdGetData, GetAgentPublicInfoAgentAgentIdGetError, GetAgentPublicInfoAgentAgentIdGetResponse, UpdateAgentAgentIdPutData, UpdateAgentAgentIdPutError, UpdateAgentAgentIdPutResponse, GetAgentSecretAgentAgentIdSecretGetData, GetAgentSecretAgentAgentIdSecretGetError, GetAgentSecretAgentAgentIdSecretGetResponse } from './types.gen';
import type { SetupAgentPostData, SetupAgentPostError, SetupAgentPostResponse, DeleteAgentDeleteData, DeleteAgentDeleteError, DeleteAgentDeleteResponse, GetAgentPublicInfoAgentAgentIdGetData, GetAgentPublicInfoAgentAgentIdGetError, GetAgentPublicInfoAgentAgentIdGetResponse, UpdateAgentAgentIdPutData, UpdateAgentAgentIdPutError, UpdateAgentAgentIdPutResponse, GetAgentSecretAgentAgentIdSecretGetData, GetAgentSecretAgentAgentIdSecretGetError, GetAgentSecretAgentAgentIdSecretGetResponse, GetAgentSecretMessageAgentAgentIdSecretMessageGetData, GetAgentSecretMessageAgentAgentIdSecretMessageGetError, GetAgentSecretMessageAgentAgentIdSecretMessageGetResponse } from './types.gen';

Check warning on line 4 in src/apis/agents/services.gen.ts

View workflow job for this annotation

GitHub Actions / build

Missed spacing between "@hey-api/client-axios" and "./types.gen" imports

Check warning on line 4 in src/apis/agents/services.gen.ts

View workflow job for this annotation

GitHub Actions / lint

Missed spacing between "@hey-api/client-axios" and "./types.gen" imports

export const client = createClient(createConfig());

Expand Down Expand Up @@ -63,4 +63,15 @@ export const getAgentSecretAgentAgentIdSecretGet = <ThrowOnError extends boolean
...options,
url: '/agent/{agent_id}/secret'
});
};

/**
* Get Agent Secret Message
* Get the message to fetch an agent secret
*/
export const getAgentSecretMessageAgentAgentIdSecretMessageGet = <ThrowOnError extends boolean = false>(options: Options<GetAgentSecretMessageAgentAgentIdSecretMessageGetData, ThrowOnError>) => {
return (options?.client ?? client).get<GetAgentSecretMessageAgentAgentIdSecretMessageGetResponse, GetAgentSecretMessageAgentAgentIdSecretMessageGetError, ThrowOnError>({
...options,
url: '/agent/{agent_id}/secret-message'
});
};
16 changes: 15 additions & 1 deletion src/apis/agents/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ export type GetAgentResponse = {
last_update: number;
};

export type GetAgentSecretMessage = {
message: string;
};

export type GetAgentSecretResponse = {
secret: string;
};
Expand Down Expand Up @@ -99,4 +103,14 @@ export type GetAgentSecretAgentAgentIdSecretGetData = {

export type GetAgentSecretAgentAgentIdSecretGetResponse = (GetAgentSecretResponse);

export type GetAgentSecretAgentAgentIdSecretGetError = (HTTPValidationError);
export type GetAgentSecretAgentAgentIdSecretGetError = (HTTPValidationError);

export type GetAgentSecretMessageAgentAgentIdSecretMessageGetData = {
path: {
agent_id: string;
};
};

export type GetAgentSecretMessageAgentAgentIdSecretMessageGetResponse = (GetAgentSecretMessage);

export type GetAgentSecretMessageAgentAgentIdSecretMessageGetError = (HTTPValidationError);
44 changes: 44 additions & 0 deletions src/apis/subscriptions/schemas.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,50 @@ export const SubsPostRefreshSubscriptionsResponseSchema = {
title: 'SubsPostRefreshSubscriptionsResponse'
} as const;

export const SubscriptionSchema = {
properties: {
id: {
type: 'string',
title: 'Id'
},
type: {
'$ref': '#/components/schemas/SubscriptionType'
},
provider: {
'$ref': '#/components/schemas/SubscriptionProvider'
},
started_at: {
type: 'integer',
title: 'Started At'
},
ended_at: {
type: 'integer',
title: 'Ended At'
},
is_active: {
type: 'boolean',
title: 'Is Active'
},
provider_data: {
type: 'object',
title: 'Provider Data'
},
account: {
'$ref': '#/components/schemas/SubscriptionAccount'
},
tags: {
items: {
type: 'string'
},
type: 'array',
title: 'Tags'
}
},
type: 'object',
required: ['id', 'type', 'provider', 'started_at', 'is_active', 'provider_data', 'account', 'tags'],
title: 'Subscription'
} as const;

export const SubscriptionAccountSchema = {
properties: {
address: {
Expand Down
13 changes: 12 additions & 1 deletion src/apis/subscriptions/services.gen.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// This file is auto-generated by @hey-api/openapi-ts

import { createClient, createConfig, type Options } from '@hey-api/client-axios';
import type { GetUserSubscriptionsSubscriptionsGetData, GetUserSubscriptionsSubscriptionsGetError, GetUserSubscriptionsSubscriptionsGetResponse, SubscribeHoldSubscriptionPostData, SubscribeHoldSubscriptionPostError, SubscribeHoldSubscriptionPostResponse, UnsubscribeHoldSubscriptionDeleteData, UnsubscribeHoldSubscriptionDeleteError, UnsubscribeHoldSubscriptionDeleteResponse, RefreshActiveHoldSubscriptionsHoldRefreshPostError, RefreshActiveHoldSubscriptionsHoldRefreshPostResponse, HoldSubscriptionMessagesHoldMessageGetData, HoldSubscriptionMessagesHoldMessageGetError, HoldSubscriptionMessagesHoldMessageGetResponse, RefreshSubsRefreshPostError, RefreshSubsRefreshPostResponse, SubscribeVouchersSubscriptionPostData, SubscribeVouchersSubscriptionPostError, SubscribeVouchersSubscriptionPostResponse, CancelVouchersSubscriptionsVouchersSubscriptionDeleteData, CancelVouchersSubscriptionsVouchersSubscriptionDeleteError, CancelVouchersSubscriptionsVouchersSubscriptionDeleteResponse, RefreshActiveVouchersSubscriptionsVouchersRefreshPostError, RefreshActiveVouchersSubscriptionsVouchersRefreshPostResponse } from './types.gen';
import type { GetUserSubscriptionsSubscriptionsGetData, GetUserSubscriptionsSubscriptionsGetError, GetUserSubscriptionsSubscriptionsGetResponse, GetSubscriptionSubscriptionsSubscriptionIdGetData, GetSubscriptionSubscriptionsSubscriptionIdGetError, GetSubscriptionSubscriptionsSubscriptionIdGetResponse, SubscribeHoldSubscriptionPostData, SubscribeHoldSubscriptionPostError, SubscribeHoldSubscriptionPostResponse, UnsubscribeHoldSubscriptionDeleteData, UnsubscribeHoldSubscriptionDeleteError, UnsubscribeHoldSubscriptionDeleteResponse, RefreshActiveHoldSubscriptionsHoldRefreshPostError, RefreshActiveHoldSubscriptionsHoldRefreshPostResponse, HoldSubscriptionMessagesHoldMessageGetData, HoldSubscriptionMessagesHoldMessageGetError, HoldSubscriptionMessagesHoldMessageGetResponse, RefreshSubsRefreshPostError, RefreshSubsRefreshPostResponse, SubscribeVouchersSubscriptionPostData, SubscribeVouchersSubscriptionPostError, SubscribeVouchersSubscriptionPostResponse, CancelVouchersSubscriptionsVouchersSubscriptionDeleteData, CancelVouchersSubscriptionsVouchersSubscriptionDeleteError, CancelVouchersSubscriptionsVouchersSubscriptionDeleteResponse, RefreshActiveVouchersSubscriptionsVouchersRefreshPostError, RefreshActiveVouchersSubscriptionsVouchersRefreshPostResponse } from './types.gen';

Check warning on line 4 in src/apis/subscriptions/services.gen.ts

View workflow job for this annotation

GitHub Actions / build

Missed spacing between "@hey-api/client-axios" and "./types.gen" imports

Check warning on line 4 in src/apis/subscriptions/services.gen.ts

View workflow job for this annotation

GitHub Actions / lint

Missed spacing between "@hey-api/client-axios" and "./types.gen" imports

export const client = createClient(createConfig());

Expand All @@ -15,6 +15,17 @@ export const getUserSubscriptionsSubscriptionsGet = <ThrowOnError extends boolea
});
};

/**
* Get Subscription
* Get a single subscription data by its ID
*/
export const getSubscriptionSubscriptionsSubscriptionIdGet = <ThrowOnError extends boolean = false>(options: Options<GetSubscriptionSubscriptionsSubscriptionIdGetData, ThrowOnError>) => {
return (options?.client ?? client).get<GetSubscriptionSubscriptionsSubscriptionIdGetResponse, GetSubscriptionSubscriptionsSubscriptionIdGetError, ThrowOnError>({
...options,
url: '/subscriptions/{subscription_id}'
});
};

/**
* Subscribe
* Subscribe to a plan
Expand Down
24 changes: 24 additions & 0 deletions src/apis/subscriptions/types.gen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,20 @@ export type SubsPostRefreshSubscriptionsResponse = {
cancelled_subscriptions: Array<(string)>;
};

export type Subscription = {
id: string;
type: SubscriptionType;
provider: SubscriptionProvider;
started_at: number;
ended_at?: number;
is_active: boolean;
provider_data: {
[key: string]: unknown;
};
account: SubscriptionAccount;
tags: Array<(string)>;
};

export type SubscriptionAccount = {
address: string;
chain: SubscriptionChain;
Expand Down Expand Up @@ -126,6 +140,16 @@ export type GetUserSubscriptionsSubscriptionsGetResponse = (GetUserSubscriptions

export type GetUserSubscriptionsSubscriptionsGetError = (HTTPValidationError);

export type GetSubscriptionSubscriptionsSubscriptionIdGetData = {
path: {
subscription_id: string;
};
};

export type GetSubscriptionSubscriptionsSubscriptionIdGetResponse = (Subscription);

export type GetSubscriptionSubscriptionsSubscriptionIdGetError = (HTTPValidationError);

export type SubscribeHoldSubscriptionPostData = {
body: HoldPostSubscriptionBody;
};
Expand Down
8 changes: 6 additions & 2 deletions src/boot/axios.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import { boot } from 'quasar/wrappers';
import { client } from 'src/apis/subscriptions/services.gen';
import { client as agentsClient } from 'src/apis/agents/services.gen';
import { client as subscriptionsClient } from 'src/apis/subscriptions/services.gen';
import env from 'src/config/env';

export default boot(() => {
client.setConfig({
subscriptionsClient.setConfig({
baseURL: env.LTAI_SUBSCRIPTIONS_API_URL,
});
agentsClient.setConfig({
baseURL: env.LTAI_AGENTS_API_URL,
});
});
18 changes: 18 additions & 0 deletions src/pages/Agents.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,18 +10,36 @@

<p>Manage your agents</p>

<q-linear-progress v-if="!agentStore.isLoaded" indeterminate />
<empty-state
v-else-if="agentStore.agents.length === 0"
description="Contact a team member to get access to the private beta"
image-alt="No agents"
image-link="/assets/empty-states/knowledge-base.png"
title="No agents"
/>
<div v-else class="tw-mt-5 tw-space-y-4">
<div v-for="agent of agentStore.agents" :key="agent.id">
<p>Agent {{ agent.id }}</p>
<a v-if="agent.vm_hash" :href="`https://aleph.sh/vm/${agent.vm_hash}`"
>https://aleph.sh/vm/{{ agent.vm_hash }}</a
>
<p v-else>Not yet deployed</p>
<p>Last update: {{ dayjs.unix(agent.last_update) }}</p>
<p v-if="agent.secret">Secret: {{ agent.secret }}</p>
<q-btn no-caps rounded @click="agentStore.getAgentSecret(agent.id)">Get secret</q-btn>
</div>
</div>
</section>
</authenticated-page>
</template>

<script lang="ts" setup>
import EmptyState from 'components/EmptyState.vue';
import LtaiIcon from 'components/libertai/LtaiIcon.vue';
import dayjs from 'dayjs';
import AuthenticatedPage from 'layouts/AuthenticatedPage.vue';
import { useAgentStore } from 'stores/agent';
const agentStore = useAgentStore();
</script>
24 changes: 19 additions & 5 deletions src/stores/account.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import * as solana from '@solana/web3.js';
import { getBalance } from '@wagmi/core';
import { getBalance, signMessage as signWagmiMessage } from '@wagmi/core';
import { defineStore } from 'pinia';
import { useWallet } from 'solana-wallets-vue';
import env from 'src/config/env';
import { config } from 'src/config/wagmi';
import { AlephPersistentStorage } from 'src/utils/aleph-persistent-storage';
import { AlephPersistentStorage, LIBERTAI_MESSAGE } from 'src/utils/aleph-persistent-storage';
import { useKnowledgeStore } from 'stores/knowledge';
import { useSettingsStore } from 'stores/settings';
import { useSubscriptionStore } from 'stores/subscription';
Expand Down Expand Up @@ -45,16 +46,29 @@ export const useAccountStore = defineStore('account', {
await Promise.all([tokensStore.update(), knowledgeStore.load(), subscriptionsStore.load()]);
},

async signMessage(message: string): Promise<`0x${string}` | string> {
if (this.account === null) {
throw Error('No account');
}

switch (this.account.chain) {
case 'base':
return signWagmiMessage(config, { message: message });
case 'solana':
const { signMessage: signSolanaMessage } = useWallet();
const signature = await signSolanaMessage.value!(Buffer.from(message));
return Buffer.from(signature).toString('base64');
}
},

async initAlephStorage() {
const settingsStore = useSettingsStore();

if (this.account === null) {
return;
}

const hash =
settingsStore.signatureHash[this.account.address] ??
(await AlephPersistentStorage.signMessage(this.account.chain));
const hash = settingsStore.signatureHash[this.account.address] ?? (await this.signMessage(LIBERTAI_MESSAGE));
if (settingsStore.isSignatureHashStored) {
settingsStore.signatureHash[this.account.address] = hash;
}
Expand Down
86 changes: 86 additions & 0 deletions src/stores/agent.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
import { defineStore } from 'pinia';
import {
getAgentPublicInfoAgentAgentIdGet,
getAgentSecretAgentAgentIdSecretGet,
getAgentSecretMessageAgentAgentIdSecretMessageGet,
} from 'src/apis/agents';
import { UIAgent } from 'src/types/agent';
import { useAccountStore } from 'stores/account';
import { useSubscriptionStore } from 'stores/subscription';

type AgentState = {
agents: UIAgent[];
isLoaded: boolean;
};

export const useAgentStore = defineStore('agents', {
state: (): AgentState => ({
agents: [],
isLoaded: false,
}),
actions: {
async load() {
const { subscriptions } = useSubscriptionStore();
const { account } = useAccountStore();

if (account === null) {
this.isLoaded = true;
return;
}

const activeAgentSubscriptions = subscriptions.filter((sub) => sub.is_active && sub.type === 'agent');

if (activeAgentSubscriptions.length === 0) {
this.isLoaded = true;
return;
}

this.agents = (
await Promise.all(
activeAgentSubscriptions.map(async (agentSubscription) => {
const agent = await getAgentPublicInfoAgentAgentIdGet({
path: { agent_id: agentSubscription.id },
});
return agent.data;
}),
)
).filter((agent) => agent !== undefined);
console.log(this.agents);

this.isLoaded = true;
},

async getAgentSecret(agentId: string) {
const { signMessage } = useAccountStore();

const messageResponse = await getAgentSecretMessageAgentAgentIdSecretMessageGet({ path: { agent_id: agentId } });

if (messageResponse.data === undefined) {
throw new Error(
messageResponse.error.detail?.toString() ?? 'Unable to fetch the message to sign to get the agent secret',
);
}

const signature = await signMessage(messageResponse.data.message);

const secretResponse = await getAgentSecretAgentAgentIdSecretGet({
path: { agent_id: agentId },
query: {
signature,
},
});

// TODO: handle errors in the call to this function
if (secretResponse.data === undefined) {
throw new Error(secretResponse.error.detail?.toString() ?? 'Unable to get the agent secret');
}

this.agents = this.agents.map((agent) => {
if (agent.id === agentId) {
return { ...agent, secret: secretResponse.data.secret };
}
return agent;
});
},
},
});
Loading

0 comments on commit 69903f2

Please sign in to comment.