mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-04-08 03:00:28 -04:00
Merge branch 'dev' into swiftyos/automat-69-add-youtube-support-to-ayrshire
This commit is contained in:
@@ -1141,3 +1141,88 @@ async def oauth_refresh_tokens(
|
||||
if response.ok:
|
||||
return OAuthTokenResponse.model_validate(response.json())
|
||||
raise ValueError(f"Failed to refresh tokens: {response.status} {response.text}")
|
||||
|
||||
|
||||
#################################################################
|
||||
# Base Management
|
||||
#################################################################
|
||||
|
||||
|
||||
async def create_base(
|
||||
credentials: Credentials,
|
||||
workspace_id: str,
|
||||
name: str,
|
||||
tables: list[dict] = [
|
||||
{
|
||||
"description": "Default table",
|
||||
"name": "Default table",
|
||||
"fields": [
|
||||
{
|
||||
"name": "ID",
|
||||
"type": "number",
|
||||
"description": "Auto-incrementing ID field",
|
||||
"options": {"precision": 0},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
) -> dict:
|
||||
"""
|
||||
Create a new base in Airtable.
|
||||
|
||||
Args:
|
||||
credentials: Airtable API credentials
|
||||
workspace_id: The workspace ID where the base will be created
|
||||
name: The name of the new base
|
||||
tables: Optional list of table objects to create in the base
|
||||
|
||||
Returns:
|
||||
dict: Response containing the created base information
|
||||
"""
|
||||
params: dict[str, Any] = {
|
||||
"name": name,
|
||||
"workspaceId": workspace_id,
|
||||
}
|
||||
|
||||
if tables:
|
||||
params["tables"] = tables
|
||||
|
||||
print(params)
|
||||
|
||||
response = await Requests().post(
|
||||
"https://api.airtable.com/v0/meta/bases",
|
||||
headers={
|
||||
"Authorization": credentials.auth_header(),
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
json=params,
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
|
||||
async def list_bases(
|
||||
credentials: Credentials,
|
||||
offset: str | None = None,
|
||||
) -> dict:
|
||||
"""
|
||||
List all bases that the authenticated user has access to.
|
||||
|
||||
Args:
|
||||
credentials: Airtable API credentials
|
||||
offset: Optional pagination offset
|
||||
|
||||
Returns:
|
||||
dict: Response containing the list of bases
|
||||
"""
|
||||
params = {}
|
||||
if offset:
|
||||
params["offset"] = offset
|
||||
|
||||
response = await Requests().get(
|
||||
"https://api.airtable.com/v0/meta/bases",
|
||||
headers={"Authorization": credentials.auth_header()},
|
||||
params=params,
|
||||
)
|
||||
|
||||
return response.json()
|
||||
|
||||
@@ -9,6 +9,7 @@ from ._api import (
|
||||
TableFieldType,
|
||||
WebhookFilters,
|
||||
WebhookSpecification,
|
||||
create_base,
|
||||
create_field,
|
||||
create_record,
|
||||
create_table,
|
||||
@@ -17,6 +18,7 @@ from ._api import (
|
||||
delete_record,
|
||||
delete_webhook,
|
||||
get_record,
|
||||
list_bases,
|
||||
list_records,
|
||||
list_webhook_payloads,
|
||||
update_field,
|
||||
@@ -38,7 +40,21 @@ async def test_create_update_table():
|
||||
api_key=SecretStr(key),
|
||||
)
|
||||
postfix = uuid4().hex[:4]
|
||||
base_id = "appSbaQLkcYiIOqux"
|
||||
workspace_id = "wsphuHmfllg7V3Brd"
|
||||
response = await create_base(credentials, workspace_id, "API Testing Base")
|
||||
assert response is not None, f"Checking create base response: {response}"
|
||||
assert (
|
||||
response.get("id") is not None
|
||||
), f"Checking create base response id: {response}"
|
||||
base_id = response.get("id")
|
||||
assert base_id is not None, f"Checking create base response id: {base_id}"
|
||||
|
||||
response = await list_bases(credentials)
|
||||
assert response is not None, f"Checking list bases response: {response}"
|
||||
assert "API Testing Base" in [
|
||||
base.get("name") for base in response.get("bases", [])
|
||||
], f"Checking list bases response bases: {response}"
|
||||
|
||||
table_name = f"test_table_{postfix}"
|
||||
table_fields = [{"name": "test_field", "type": "singleLineText"}]
|
||||
table = await create_table(credentials, base_id, table_name, table_fields)
|
||||
@@ -73,7 +89,7 @@ async def test_invalid_field_type():
|
||||
api_key=SecretStr(key),
|
||||
)
|
||||
postfix = uuid4().hex[:4]
|
||||
base_id = "appSbaQLkcYiIOqux"
|
||||
base_id = "appZPxegHEU3kDc1S"
|
||||
table_name = f"test_table_{postfix}"
|
||||
table_fields = [{"name": "test_field", "type": "notValid"}]
|
||||
with pytest.raises(AssertionError):
|
||||
@@ -91,7 +107,7 @@ async def test_create_and_update_field():
|
||||
api_key=SecretStr(key),
|
||||
)
|
||||
postfix = uuid4().hex[:4]
|
||||
base_id = "appSbaQLkcYiIOqux"
|
||||
base_id = "appZPxegHEU3kDc1S"
|
||||
table_name = f"test_table_{postfix}"
|
||||
table_fields = [{"name": "test_field", "type": "singleLineText"}]
|
||||
table = await create_table(credentials, base_id, table_name, table_fields)
|
||||
@@ -133,7 +149,7 @@ async def test_record_management():
|
||||
api_key=SecretStr(key),
|
||||
)
|
||||
postfix = uuid4().hex[:4]
|
||||
base_id = "appSbaQLkcYiIOqux"
|
||||
base_id = "appZPxegHEU3kDc1S"
|
||||
table_name = f"test_table_{postfix}"
|
||||
table_fields = [{"name": "test_field", "type": "singleLineText"}]
|
||||
table = await create_table(credentials, base_id, table_name, table_fields)
|
||||
@@ -261,7 +277,7 @@ async def test_webhook_management():
|
||||
api_key=SecretStr(key),
|
||||
)
|
||||
postfix = uuid4().hex[:4]
|
||||
base_id = "appSbaQLkcYiIOqux"
|
||||
base_id = "appZPxegHEU3kDc1S"
|
||||
table_name = f"test_table_{postfix}"
|
||||
table_fields = [{"name": "test_field", "type": "singleLineText"}]
|
||||
table = await create_table(credentials, base_id, table_name, table_fields)
|
||||
|
||||
122
autogpt_platform/backend/backend/blocks/airtable/bases.py
Normal file
122
autogpt_platform/backend/backend/blocks/airtable/bases.py
Normal file
@@ -0,0 +1,122 @@
|
||||
"""
|
||||
Airtable base operation blocks.
|
||||
"""
|
||||
|
||||
from typing import Optional
|
||||
|
||||
from backend.sdk import (
|
||||
APIKeyCredentials,
|
||||
Block,
|
||||
BlockCategory,
|
||||
BlockOutput,
|
||||
BlockSchema,
|
||||
CredentialsMetaInput,
|
||||
SchemaField,
|
||||
)
|
||||
|
||||
from ._api import create_base, list_bases
|
||||
from ._config import airtable
|
||||
|
||||
|
||||
class AirtableCreateBaseBlock(Block):
|
||||
"""
|
||||
Creates a new base in an Airtable workspace.
|
||||
"""
|
||||
|
||||
class Input(BlockSchema):
|
||||
credentials: CredentialsMetaInput = airtable.credentials_field(
|
||||
description="Airtable API credentials"
|
||||
)
|
||||
workspace_id: str = SchemaField(
|
||||
description="The workspace ID where the base will be created"
|
||||
)
|
||||
name: str = SchemaField(description="The name of the new base")
|
||||
tables: list[dict] = SchemaField(
|
||||
description="At least one table and field must be specified. Array of table objects to create in the base. Each table should have 'name' and 'fields' properties",
|
||||
default=[
|
||||
{
|
||||
"description": "Default table",
|
||||
"name": "Default table",
|
||||
"fields": [
|
||||
{
|
||||
"name": "ID",
|
||||
"type": "number",
|
||||
"description": "Auto-incrementing ID field",
|
||||
"options": {"precision": 0},
|
||||
}
|
||||
],
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
class Output(BlockSchema):
|
||||
base_id: str = SchemaField(description="The ID of the created base")
|
||||
tables: list[dict] = SchemaField(description="Array of table objects")
|
||||
table: dict = SchemaField(description="A single table object")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
id="f59b88a8-54ce-4676-a508-fd614b4e8dce",
|
||||
description="Create a new base in Airtable",
|
||||
categories={BlockCategory.DATA},
|
||||
input_schema=self.Input,
|
||||
output_schema=self.Output,
|
||||
)
|
||||
|
||||
async def run(
|
||||
self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs
|
||||
) -> BlockOutput:
|
||||
data = await create_base(
|
||||
credentials,
|
||||
input_data.workspace_id,
|
||||
input_data.name,
|
||||
input_data.tables,
|
||||
)
|
||||
|
||||
yield "base_id", data.get("id", None)
|
||||
yield "tables", data.get("tables", [])
|
||||
for table in data.get("tables", []):
|
||||
yield "table", table
|
||||
|
||||
|
||||
class AirtableListBasesBlock(Block):
|
||||
"""
|
||||
Lists all bases in an Airtable workspace that the user has access to.
|
||||
"""
|
||||
|
||||
class Input(BlockSchema):
|
||||
credentials: CredentialsMetaInput = airtable.credentials_field(
|
||||
description="Airtable API credentials"
|
||||
)
|
||||
trigger: str = SchemaField(
|
||||
description="Trigger the block to run - value is ignored", default="manual"
|
||||
)
|
||||
offset: str = SchemaField(
|
||||
description="Pagination offset from previous request", default=""
|
||||
)
|
||||
|
||||
class Output(BlockSchema):
|
||||
bases: list[dict] = SchemaField(description="Array of base objects")
|
||||
offset: Optional[str] = SchemaField(
|
||||
description="Offset for next page (null if no more bases)", default=None
|
||||
)
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
id="4bd8d466-ed5d-4e44-8083-97f25a8044e7",
|
||||
description="List all bases in Airtable",
|
||||
categories={BlockCategory.DATA},
|
||||
input_schema=self.Input,
|
||||
output_schema=self.Output,
|
||||
)
|
||||
|
||||
async def run(
|
||||
self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs
|
||||
) -> BlockOutput:
|
||||
data = await list_bases(
|
||||
credentials,
|
||||
offset=input_data.offset if input_data.offset else None,
|
||||
)
|
||||
|
||||
yield "bases", data.get("bases", [])
|
||||
yield "offset", data.get("offset", None)
|
||||
14
autogpt_platform/backend/backend/blocks/wolfram/_api.py
Normal file
14
autogpt_platform/backend/backend/blocks/wolfram/_api.py
Normal file
@@ -0,0 +1,14 @@
|
||||
from backend.sdk import APIKeyCredentials, Requests
|
||||
|
||||
|
||||
async def llm_api_call(credentials: APIKeyCredentials, question: str) -> str:
|
||||
params = {"appid": credentials.api_key.get_secret_value(), "input": question}
|
||||
response = await Requests().get(
|
||||
"https://www.wolframalpha.com/api/v1/llm-api", params=params
|
||||
)
|
||||
if not response.ok:
|
||||
raise ValueError(f"API request failed: {response.status} {response.text()}")
|
||||
|
||||
answer = response.text() if response.text() else ""
|
||||
|
||||
return answer
|
||||
50
autogpt_platform/backend/backend/blocks/wolfram/llm_api.py
Normal file
50
autogpt_platform/backend/backend/blocks/wolfram/llm_api.py
Normal file
@@ -0,0 +1,50 @@
|
||||
from backend.sdk import (
|
||||
APIKeyCredentials,
|
||||
Block,
|
||||
BlockCategory,
|
||||
BlockCostType,
|
||||
BlockOutput,
|
||||
BlockSchema,
|
||||
CredentialsMetaInput,
|
||||
ProviderBuilder,
|
||||
SchemaField,
|
||||
)
|
||||
|
||||
from ._api import llm_api_call
|
||||
|
||||
wolfram = (
|
||||
ProviderBuilder("wolfram")
|
||||
.with_api_key("WOLFRAM_APP_ID", "Wolfram Alpha App ID")
|
||||
.with_base_cost(1, BlockCostType.RUN)
|
||||
.build()
|
||||
)
|
||||
|
||||
|
||||
class AskWolframBlock(Block):
|
||||
"""
|
||||
Ask Wolfram Alpha a question.
|
||||
"""
|
||||
|
||||
class Input(BlockSchema):
|
||||
credentials: CredentialsMetaInput = wolfram.credentials_field(
|
||||
description="Wolfram Alpha API credentials"
|
||||
)
|
||||
question: str = SchemaField(description="The question to ask")
|
||||
|
||||
class Output(BlockSchema):
|
||||
answer: str = SchemaField(description="The answer to the question")
|
||||
|
||||
def __init__(self):
|
||||
super().__init__(
|
||||
id="b7710ce4-68ef-4e82-9a2f-f0b874ef9c7d",
|
||||
description="Ask Wolfram Alpha a question",
|
||||
categories={BlockCategory.SEARCH},
|
||||
input_schema=self.Input,
|
||||
output_schema=self.Output,
|
||||
)
|
||||
|
||||
async def run(
|
||||
self, input_data: Input, *, credentials: APIKeyCredentials, **kwargs
|
||||
) -> BlockOutput:
|
||||
answer = await llm_api_call(credentials, input_data.question)
|
||||
yield "answer", answer
|
||||
796
autogpt_platform/frontend/pnpm-lock.yaml
generated
796
autogpt_platform/frontend/pnpm-lock.yaml
generated
File diff suppressed because it is too large
Load Diff
@@ -27,8 +27,6 @@ import type { CreatorDetails } from "../../models/creatorDetails";
|
||||
|
||||
import type { CreatorsResponse } from "../../models/creatorsResponse";
|
||||
|
||||
import type { GetV2GetMyAgentsParams } from "../../models/getV2GetMyAgentsParams";
|
||||
|
||||
import type { GetV2ListMySubmissionsParams } from "../../models/getV2ListMySubmissionsParams";
|
||||
|
||||
import type { GetV2ListStoreAgentsParams } from "../../models/getV2ListStoreAgentsParams";
|
||||
@@ -1952,78 +1950,45 @@ export type getV2GetMyAgentsResponse200 = {
|
||||
status: 200;
|
||||
};
|
||||
|
||||
export type getV2GetMyAgentsResponse422 = {
|
||||
data: HTTPValidationError;
|
||||
status: 422;
|
||||
};
|
||||
|
||||
export type getV2GetMyAgentsResponseComposite =
|
||||
| getV2GetMyAgentsResponse200
|
||||
| getV2GetMyAgentsResponse422;
|
||||
export type getV2GetMyAgentsResponseComposite = getV2GetMyAgentsResponse200;
|
||||
|
||||
export type getV2GetMyAgentsResponse = getV2GetMyAgentsResponseComposite & {
|
||||
headers: Headers;
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsUrl = (params?: GetV2GetMyAgentsParams) => {
|
||||
const normalizedParams = new URLSearchParams();
|
||||
|
||||
Object.entries(params || {}).forEach(([key, value]) => {
|
||||
if (value !== undefined) {
|
||||
normalizedParams.append(key, value === null ? "null" : value.toString());
|
||||
}
|
||||
});
|
||||
|
||||
const stringifiedParams = normalizedParams.toString();
|
||||
|
||||
return stringifiedParams.length > 0
|
||||
? `/api/store/myagents?${stringifiedParams}`
|
||||
: `/api/store/myagents`;
|
||||
export const getGetV2GetMyAgentsUrl = () => {
|
||||
return `/api/store/myagents`;
|
||||
};
|
||||
|
||||
export const getV2GetMyAgents = async (
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: RequestInit,
|
||||
): Promise<getV2GetMyAgentsResponse> => {
|
||||
return customMutator<getV2GetMyAgentsResponse>(
|
||||
getGetV2GetMyAgentsUrl(params),
|
||||
{
|
||||
...options,
|
||||
method: "GET",
|
||||
},
|
||||
);
|
||||
return customMutator<getV2GetMyAgentsResponse>(getGetV2GetMyAgentsUrl(), {
|
||||
...options,
|
||||
method: "GET",
|
||||
});
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsQueryKey = (
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
) => {
|
||||
return [`/api/store/myagents`, ...(params ? [params] : [])] as const;
|
||||
export const getGetV2GetMyAgentsQueryKey = () => {
|
||||
return [`/api/store/myagents`] as const;
|
||||
};
|
||||
|
||||
export const getGetV2GetMyAgentsQueryOptions = <
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError,
|
||||
TData
|
||||
>
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
) => {
|
||||
TError = unknown,
|
||||
>(options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<Awaited<ReturnType<typeof getV2GetMyAgents>>, TError, TData>
|
||||
>;
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
}) => {
|
||||
const { query: queryOptions, request: requestOptions } = options ?? {};
|
||||
|
||||
const queryKey =
|
||||
queryOptions?.queryKey ?? getGetV2GetMyAgentsQueryKey(params);
|
||||
const queryKey = queryOptions?.queryKey ?? getGetV2GetMyAgentsQueryKey();
|
||||
|
||||
const queryFn: QueryFunction<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>
|
||||
> = ({ signal }) => getV2GetMyAgents(params, { signal, ...requestOptions });
|
||||
> = ({ signal }) => getV2GetMyAgents({ signal, ...requestOptions });
|
||||
|
||||
return { queryKey, queryFn, ...queryOptions } as UseQueryOptions<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
@@ -2035,13 +2000,12 @@ export const getGetV2GetMyAgentsQueryOptions = <
|
||||
export type GetV2GetMyAgentsQueryResult = NonNullable<
|
||||
Awaited<ReturnType<typeof getV2GetMyAgents>>
|
||||
>;
|
||||
export type GetV2GetMyAgentsQueryError = HTTPValidationError;
|
||||
export type GetV2GetMyAgentsQueryError = unknown;
|
||||
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params: undefined | GetV2GetMyAgentsParams,
|
||||
options: {
|
||||
query: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2066,9 +2030,8 @@ export function useGetV2GetMyAgents<
|
||||
};
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2093,9 +2056,8 @@ export function useGetV2GetMyAgents<
|
||||
};
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2116,9 +2078,8 @@ export function useGetV2GetMyAgents<
|
||||
|
||||
export function useGetV2GetMyAgents<
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2133,7 +2094,7 @@ export function useGetV2GetMyAgents<
|
||||
): UseQueryResult<TData, TError> & {
|
||||
queryKey: DataTag<QueryKey, TData, TError>;
|
||||
} {
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(params, options);
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(options);
|
||||
|
||||
const query = useQuery(queryOptions, queryClient) as UseQueryResult<
|
||||
TData,
|
||||
@@ -2150,10 +2111,9 @@ export function useGetV2GetMyAgents<
|
||||
*/
|
||||
export const prefetchGetV2GetMyAgentsQuery = async <
|
||||
TData = Awaited<ReturnType<typeof getV2GetMyAgents>>,
|
||||
TError = HTTPValidationError,
|
||||
TError = unknown,
|
||||
>(
|
||||
queryClient: QueryClient,
|
||||
params?: GetV2GetMyAgentsParams,
|
||||
options?: {
|
||||
query?: Partial<
|
||||
UseQueryOptions<
|
||||
@@ -2165,7 +2125,7 @@ export const prefetchGetV2GetMyAgentsQuery = async <
|
||||
request?: SecondParameter<typeof customMutator>;
|
||||
},
|
||||
): Promise<QueryClient> => {
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(params, options);
|
||||
const queryOptions = getGetV2GetMyAgentsQueryOptions(options);
|
||||
|
||||
await queryClient.prefetchQuery(queryOptions);
|
||||
|
||||
|
||||
@@ -2467,30 +2467,6 @@
|
||||
"tags": ["v2", "store", "private"],
|
||||
"summary": "Get my agents",
|
||||
"operationId": "getV2Get my agents",
|
||||
"parameters": [
|
||||
{
|
||||
"name": "page",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 1,
|
||||
"title": "Page"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": "page_size",
|
||||
"in": "query",
|
||||
"required": false,
|
||||
"schema": {
|
||||
"type": "integer",
|
||||
"minimum": 1,
|
||||
"default": 20,
|
||||
"title": "Page Size"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"description": "Successful Response",
|
||||
@@ -2499,14 +2475,6 @@
|
||||
"schema": { "$ref": "#/components/schemas/MyAgentsResponse" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"422": {
|
||||
"description": "Validation Error",
|
||||
"content": {
|
||||
"application/json": {
|
||||
"schema": { "$ref": "#/components/schemas/HTTPValidationError" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,17 +75,17 @@ export const PublishAgentPopout: React.FC<PublishAgentPopoutProps> = ({
|
||||
const api = useBackendAPI();
|
||||
|
||||
// Use the auto-generated API hook
|
||||
const { data, error, isLoading, refetch } = useGetV2GetMyAgents(
|
||||
{
|
||||
page: currentPage,
|
||||
page_size: 20,
|
||||
},
|
||||
{
|
||||
query: {
|
||||
enabled: open, // Only fetch when the popout is open
|
||||
const { data, error, isLoading, refetch } = useGetV2GetMyAgents({
|
||||
request: {
|
||||
params: {
|
||||
page: currentPage,
|
||||
page_size: 20,
|
||||
},
|
||||
},
|
||||
);
|
||||
query: {
|
||||
enabled: open, // Only fetch when the popout is open
|
||||
},
|
||||
});
|
||||
|
||||
// Update allAgents when new data arrives
|
||||
React.useEffect(() => {
|
||||
|
||||
@@ -79,18 +79,11 @@ export function ActivityItem({ execution }: Props) {
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
const agentId = execution.library_agent_id || execution.graph_id;
|
||||
const linkUrl = `/library/agents/${agentId}?executionId=${execution.id}`;
|
||||
const withLibraryId = !!execution.library_agent_id;
|
||||
const linkUrl = `/library/agents/${execution.library_agent_id}?executionId=${execution.id}`;
|
||||
const withExecutionLink = execution.library_agent_id && execution.id;
|
||||
|
||||
if (!withLibraryId) return null;
|
||||
|
||||
return (
|
||||
<Link
|
||||
className="block cursor-pointer border-b border-slate-50 px-2 py-3 transition-colors last:border-b-0 hover:bg-lightGrey"
|
||||
href={linkUrl}
|
||||
role="button"
|
||||
>
|
||||
const content = (
|
||||
<>
|
||||
{/* Icon + Agent Name */}
|
||||
<div className="flex items-center space-x-2">
|
||||
{getStatusIcon()}
|
||||
@@ -109,6 +102,20 @@ export function ActivityItem({ execution }: Props) {
|
||||
{getTimeDisplay()}
|
||||
</Text>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
||||
return withExecutionLink ? (
|
||||
<Link
|
||||
className="block cursor-pointer border-b border-slate-50 px-2 py-3 transition-colors last:border-b-0 hover:bg-lightGrey"
|
||||
href={linkUrl}
|
||||
role="button"
|
||||
>
|
||||
{content}
|
||||
</Link>
|
||||
) : (
|
||||
<div className="block border-b border-slate-50 px-2 py-3 last:border-b-0">
|
||||
{content}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { useGetV1GetAllExecutions } from "@/app/api/__generated__/endpoints/graphs/graphs";
|
||||
import { useGetV2GetMyAgents } from "@/app/api/__generated__/endpoints/store/store";
|
||||
import { useGetV2ListLibraryAgents } from "@/app/api/__generated__/endpoints/library/library";
|
||||
import { LibraryAgent } from "@/app/api/__generated__/models/libraryAgent";
|
||||
|
||||
import { LibraryAgentResponse } from "@/app/api/__generated__/models/libraryAgentResponse";
|
||||
import { MyAgentsResponse } from "@/app/api/__generated__/models/myAgentsResponse";
|
||||
import { MyAgent } from "@/app/api/__generated__/models/myAgent";
|
||||
import BackendAPI from "@/lib/autogpt-server-api/client";
|
||||
import type { GraphExecution, GraphID } from "@/lib/autogpt-server-api/types";
|
||||
import { useCallback, useEffect, useState } from "react";
|
||||
import * as Sentry from "@sentry/nextjs";
|
||||
import {
|
||||
NotificationState,
|
||||
categorizeExecutions,
|
||||
createAgentInfoMap,
|
||||
handleExecutionUpdate,
|
||||
} from "./helpers";
|
||||
|
||||
@@ -22,80 +19,55 @@ type AgentInfoMap = Map<
|
||||
|
||||
export function useAgentActivityDropdown() {
|
||||
const [api] = useState(() => new BackendAPI());
|
||||
|
||||
const [notifications, setNotifications] = useState<NotificationState>({
|
||||
activeExecutions: [],
|
||||
recentCompletions: [],
|
||||
recentFailures: [],
|
||||
totalCount: 0,
|
||||
});
|
||||
|
||||
const [isConnected, setIsConnected] = useState(false);
|
||||
const [agentInfoMap, setAgentInfoMap] = useState<AgentInfoMap>(new Map());
|
||||
|
||||
// Get library agents using generated hook
|
||||
const {
|
||||
data: myAgentsResponse,
|
||||
isLoading: isAgentsLoading,
|
||||
data: agents,
|
||||
isSuccess: agentsSuccess,
|
||||
error: agentsError,
|
||||
} = useGetV2GetMyAgents(
|
||||
{},
|
||||
{
|
||||
// Enable query by default
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
} = useGetV2ListLibraryAgents();
|
||||
|
||||
// Get library agents data to map graph_id to library_agent_id
|
||||
const {
|
||||
data: libraryAgentsResponse,
|
||||
isLoading: isLibraryAgentsLoading,
|
||||
error: libraryAgentsError,
|
||||
} = useGetV2ListLibraryAgents(
|
||||
{},
|
||||
{
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
// Get all executions using generated hook
|
||||
const {
|
||||
data: executionsResponse,
|
||||
isLoading: isExecutionsLoading,
|
||||
data: executions,
|
||||
isSuccess: executionsSuccess,
|
||||
error: executionsError,
|
||||
} = useGetV1GetAllExecutions({
|
||||
query: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
} = useGetV1GetAllExecutions();
|
||||
|
||||
// Update agent info map when both agent data sources change
|
||||
// Create a map of library agents
|
||||
useEffect(() => {
|
||||
if (myAgentsResponse?.data && libraryAgentsResponse?.data) {
|
||||
const myAgents = myAgentsResponse.data as MyAgentsResponse;
|
||||
const libraryAgents = libraryAgentsResponse.data as LibraryAgentResponse;
|
||||
if (agents && agentsSuccess) {
|
||||
// SafeCast: library agents loaded
|
||||
const libraryAgents = agents.data as LibraryAgentResponse;
|
||||
|
||||
if (myAgents?.agents && libraryAgents?.agents) {
|
||||
const agentMap = createAgentInfoMap(myAgents.agents);
|
||||
if (!libraryAgents.agents || !libraryAgents.agents.length) return;
|
||||
|
||||
libraryAgents.agents.forEach((libraryAgent: LibraryAgent) => {
|
||||
if (libraryAgent.graph_id && libraryAgent.id) {
|
||||
const existingInfo = agentMap.get(libraryAgent.graph_id);
|
||||
if (existingInfo) {
|
||||
agentMap.set(libraryAgent.graph_id, {
|
||||
...existingInfo,
|
||||
library_agent_id: libraryAgent.id,
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
const agentMap = new Map<
|
||||
string,
|
||||
{ name: string; description: string; library_agent_id?: string }
|
||||
>();
|
||||
|
||||
setAgentInfoMap(agentMap);
|
||||
}
|
||||
libraryAgents.agents.forEach((agent) => {
|
||||
if (agent.graph_id && agent.id) {
|
||||
agentMap.set(agent.graph_id, {
|
||||
name: agent.name || `Agent ${agent.graph_id.slice(0, 8)}`,
|
||||
description: agent.description || "",
|
||||
library_agent_id: agent.id,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
setAgentInfoMap(agentMap);
|
||||
}
|
||||
}, [myAgentsResponse, libraryAgentsResponse]);
|
||||
}, [agents, agentsSuccess]);
|
||||
|
||||
// Handle real-time execution updates
|
||||
const handleExecutionEvent = useCallback(
|
||||
@@ -109,41 +81,27 @@ export function useAgentActivityDropdown() {
|
||||
|
||||
// Process initial execution state when data loads
|
||||
useEffect(() => {
|
||||
if (
|
||||
executionsResponse?.data &&
|
||||
!isExecutionsLoading &&
|
||||
agentInfoMap.size > 0
|
||||
) {
|
||||
const newNotifications = categorizeExecutions(
|
||||
executionsResponse.data,
|
||||
agentInfoMap,
|
||||
);
|
||||
|
||||
setNotifications(newNotifications);
|
||||
if (executions && executionsSuccess && agentInfoMap.size > 0) {
|
||||
const notifications = categorizeExecutions(executions.data, agentInfoMap);
|
||||
setNotifications(notifications);
|
||||
}
|
||||
}, [executionsResponse, isExecutionsLoading, agentInfoMap]);
|
||||
}, [executions, executionsSuccess, agentInfoMap]);
|
||||
|
||||
// Initialize WebSocket connection for real-time updates
|
||||
useEffect(() => {
|
||||
if (!agentInfoMap.size) return;
|
||||
|
||||
const connectHandler = api.onWebSocketConnect(() => {
|
||||
setIsConnected(true);
|
||||
|
||||
if (myAgentsResponse?.data) {
|
||||
const myAgents = myAgentsResponse.data as MyAgentsResponse;
|
||||
|
||||
if (myAgents?.agents) {
|
||||
myAgents.agents.forEach((agent: MyAgent) => {
|
||||
api
|
||||
.subscribeToGraphExecutions(agent.agent_id as GraphID)
|
||||
.catch((error) => {
|
||||
console.error(
|
||||
`[AgentNotifications] Failed to subscribe to graph ${agent.agent_id}:`,
|
||||
error,
|
||||
);
|
||||
});
|
||||
agentInfoMap.forEach((_, graphId) => {
|
||||
api.subscribeToGraphExecutions(graphId as GraphID).catch((error) => {
|
||||
Sentry.captureException(error, {
|
||||
tags: {
|
||||
graphId,
|
||||
},
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
const disconnectHandler = api.onWebSocketDisconnect(() => {
|
||||
@@ -163,12 +121,12 @@ export function useAgentActivityDropdown() {
|
||||
messageHandler();
|
||||
api.disconnectWebSocket();
|
||||
};
|
||||
}, [api, handleExecutionEvent, myAgentsResponse]);
|
||||
}, [api, handleExecutionEvent, agentInfoMap]);
|
||||
|
||||
return {
|
||||
...notifications,
|
||||
isConnected,
|
||||
isLoading: isAgentsLoading || isExecutionsLoading || isLibraryAgentsLoading,
|
||||
error: agentsError || executionsError || libraryAgentsError,
|
||||
isReady: executionsSuccess && agentsSuccess,
|
||||
error: executionsError || agentsError,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user