Skip to content
Draft
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
187 changes: 163 additions & 24 deletions auth4genai/snippets/get-started/cloudflare-agents-js/call-your-api.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,27 @@ npm install hono \

In the root directory of your project, copy the `.dev.vars.example` into `.dev.vars` file and configure the Auth0 and OpenAI variables.

```bash .dev.vars wrap lines
# ...
# You can use any provider of your choice supported by Vercel AI
OPENAI_API_KEY="OPENAI API KEY"

SESSION_STORE=cloudflare-kv
SESSION_STORE_NAMESPACE=Session


#auth0
AUTH0_DOMAIN="YOUR-ACCOUNT.us.auth0.com"
AUTH0_CLIENT_ID="YOUR CLIENT ID"
AUTH0_CLIENT_SECRET="YOUR CLIENT SECRET"
AUTH0_SESSION_ENCRYPTION_KEY="RANDOM 32 CHARS"
AUTH0_AUDIENCE="YOUR AUDIENCE"

BASE_URL="http://localhost:3000"
```

If you use another provider for your LLM, adjust the variable name in `.dev.vars` accordingly.

### Define a tool to call your API

In this step, you'll create a Vercel AI tool to make the first-party API call to the Auth0 API. You will do the same for third-party APIs.
Expand All @@ -53,41 +74,159 @@ Since the Agent defined in the class Chat in `src/agent/chat.ts` uses the **Auth
The tool we are defining here uses the same access token to call Auth0's [`/userinfo`](https://auth0.com/docs/api/authentication/user-profile/get-user-info) endpoint.

```tsx src/agent/tools.ts wrap lines
const getUserInfoTool = tool({
description: "Get information about the current logged in user.",
parameters: z.object({}),
execute: async () => {
const { agent } = getCurrentAgent<Chat>();
const tokenSet = agent?.getCredentials();
if (!tokenSet) {
return "There is no user logged in.";
/**
* Tool definitions for the AI chat agent
* Tools can either require human confirmation or execute automatically
*/
import { tool } from "ai";
import { z } from "zod";

import { getCurrentAgent } from "agents";
import { unstable_scheduleSchema } from "agents/schedule";
import { format, toZonedTime } from "date-fns-tz";
import { buyStock } from "./auth0-ai-sample-tools/buy-stock";
import { checkUsersCalendar } from "./auth0-ai-sample-tools/check-user-calendar";

/**
* Weather information tool that requires human confirmation
* When invoked, this will present a confirmation dialog to the user
* The actual implementation is in the executions object below
*/
const getWeatherInformation = tool({
description: "show the weather in a given city to the user",
inputSchema: z.object({ city: z.string() }),
});

/**
* Local time tool that executes automatically
* Since it includes an execute function, it will run without user confirmation
* This is suitable for low-risk operations that don't need oversight
*/
const getLocalTime = tool({
description: "get the local time for a specified location",
inputSchema: z.object({
timeZone: z.string().describe("IANA time zone name"),
}),
execute: async ({ timeZone: location }) => {
const now = new Date();
const zonedDate = toZonedTime(now, location);
const output = format(zonedDate, "yyyy-MM-dd HH:mm:ssXXX", {
timeZone: location,
});
return output;
},
});

const scheduleTask = tool({
description: "A tool to schedule a task to be executed at a later time",
inputSchema: unstable_scheduleSchema,
execute: async ({ when, description }) => {
const { agent } = getCurrentAgent();

function throwError(msg: string): string {
throw new Error(msg);
}
if (when.type === "no-schedule") {
return "Not a valid schedule input";
}
const input =
when.type === "scheduled"
? when.date // scheduled
: when.type === "delayed"
? when.delayInSeconds // delayed
: when.type === "cron"
? when.cron // cron
: throwError("not a valid schedule input");
try {
agent!.schedule(input!, "executeTask" as keyof typeof agent, description);
} catch (error) {
console.error("error scheduling task", error);
return `Error scheduling task: ${error}`;
}
return `Task scheduled for type "${when.type}" : ${input}`;
},
});

const response = await fetch(
`https://${process.env.AUTH0_DOMAIN}/userinfo`,
{
headers: {
Authorization: `Bearer ${tokenSet.access_token}`,
},
}
);
/**
* Tool to list all scheduled tasks
* This executes automatically without requiring human confirmation
*/
const getScheduledTasks = tool({
description: "List all tasks that have been scheduled",
inputSchema: z.object({}),
execute: async () => {
const { agent } = getCurrentAgent();

if (response.ok) {
return { result: await response.json() };
try {
const tasks = agent!.getSchedules();
if (!tasks || tasks.length === 0) {
return "No scheduled tasks found.";
}
return tasks;
} catch (error) {
console.error("Error listing scheduled tasks", error);
return `Error listing scheduled tasks: ${error}`;
}
},
});

return "I couldn't verify your identity";
/**
* Tool to cancel a scheduled task by its ID
* This executes automatically without requiring human confirmation
*/
const cancelScheduledTask = tool({
description: "Cancel a scheduled task using its ID",
inputSchema: z.object({
taskId: z.string().describe("The ID of the task to cancel"),
}),
execute: async ({ taskId }) => {
const { agent } = getCurrentAgent();
try {
await agent!.cancelSchedule(taskId);
return `Task ${taskId} has been successfully canceled.`;
} catch (error) {
console.error("Error canceling scheduled task", error);
return `Error canceling task ${taskId}: ${error}`;
}
},
});

/**
* Export all available tools
* These will be provided to the AI model to describe available capabilities
*/
export const tools = {
getWeatherInformation,
getLocalTime,
scheduleTask,
getScheduledTasks,
cancelScheduledTask,
checkUsersCalendar,
buyStock,
};

/**
* Implementation of confirmation-required tools
* This object contains the actual logic for tools that need human approval
* Each function here corresponds to a tool above that doesn't have an execute function
*/
export const executions = {
getWeatherInformation: async ({ city }: { city: string }) => {
console.log(`Getting weather information for ${city}`);
return `The weather in ${city} is sunny`;
},
};
```

Then in the `tools` export of the `src/agent/chat.ts` file, add the `getUserInfoTool` to the tools array:
Then in the `tools` export of the `src/agent/chat.ts` file, add the `tools` to the `allTools` array:

```tsx src/agent/chat.ts wrap lines
export const tools = {
// Your other tools...
getUserInfoTool,
};
async onChatMessage() {
const allTools = {
...tools,
...(this.mcp?.getAITools?.() ?? {}),
};
... // The rest of the code
```

### Test your application
Expand Down
43 changes: 23 additions & 20 deletions auth4genai/snippets/how-tos/github/cloudflare-agents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -19,50 +19,55 @@ npm install @auth0/ai-vercel @auth0/ai-cloudflare @auth0/ai

Then, you need to initialize Auth0 AI and set up the connection to request access tokens with the required Github scopes.

```typescript ./src/lib/auth0-ai.ts wrap lines
```typescript ./src/agent/auth0-ai.ts wrap lines
import { Auth0AI, setGlobalAIContext } from "@auth0/ai-vercel";
import { getCurrentAgent } from "agents";
import type { Chat } from "./chat";

setGlobalAIContext(() => ({ threadID: getAgent().name }));

const auth0AI = new Auth0AI({
store: () => {
return (getAgent() as any).auth0AIStore;
},
});

const getAgent = () => {
const { agent } = getCurrentAgent<Chat>();
const { agent } = getCurrentAgent();
if (!agent) {
throw new Error("No agent found");
}
return agent;
};

setGlobalAIContext(() => ({ threadID: getAgent().name }));

const auth0AI = new Auth0AI();
const refreshToken = async () => {
const credentials = (getAgent() as any).getCredentials();
return credentials?.refresh_token;
};

export const withGitHub = auth0AI.withTokenVault({
refreshToken,
connection: "github",
scopes: ["repo"],
refreshToken: async () => {
const credentials = getAgent().getCredentials();
return credentials?.refresh_token;
},
});
```

### 2. Integrate your tool with the Github API

Wrap your tool using the Auth0 AI SDK to obtain an access token for the Github API.

```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={2-4,9,15,19-21,31-33}
```typescript ./src/agent/tools/listRepositories.ts wrap lines highlight={6-7,9,15,19-21,28-30}
import { tool } from "ai";
import { z } from "zod/v3";

import { Octokit, RequestError } from "octokit";
import { getAccessTokenFromTokenVault } from "@auth0/ai-vercel";
import { TokenVaultError } from "@auth0/ai/interrupts";
import { withGitHub } from "@/lib/auth0-ai";
import { tool } from "ai";
import { z } from "zod";

import { withGitHub } from "@/agent/auth0-ai";

export const listRepositories = withGitHub(
tool({
description: "List respositories for the current user on GitHub",
parameters: z.object({}),
inputSchema: z.object({}),
execute: async () => {
// Get the access token from Auth0 AI
const accessToken = getAccessTokenFromTokenVault();
Expand All @@ -72,17 +77,14 @@ export const listRepositories = withGitHub(
const octokit = new Octokit({
auth: accessToken,
});

const { data } = await octokit.rest.repos.listForAuthenticatedUser();

return data.map((repo) => repo.name);
} catch (error) {
console.log("Error", error);

if (error instanceof RequestError) {
if (error.status === 401) {
throw new TokenVaultError(
`Authorization required to access the Token Vault connection`
`Authorization required to access the Token Vault`
);
}
}
Expand All @@ -92,6 +94,7 @@ export const listRepositories = withGitHub(
},
})
);

```

### 3. Handle authentication redirects
Expand Down
Loading