Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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
21 changes: 21 additions & 0 deletions src/custom/CortiClient.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { CortiClient as BaseCortiClient } from "../Client.js";
import type * as environments from "../environments.js";
import * as core from "../core/index.js";
import { CortiAuth } from "./auth/CortiAuth.js";
import { CustomAgents } from "./agents/CustomAgents.js";
import { CustomStream } from "./stream/CustomStream.js";
import { CustomTranscribe } from "./transcribe/CustomTranscribe.js";
import { authToBaseOptions } from "./utils/authToBaseOptions.js";
import { type Environment, getEnvironment } from "./utils/environment.js";

import { resolveClientOptions } from "./utils/resolveClientOptions.js";
import { setDefaultWithCredentials } from "./utils/withCredentialsConfig.js";

Expand Down Expand Up @@ -41,6 +44,7 @@ export class CortiClient extends BaseCortiClient {
protected override _auth: CortiAuth | undefined;
protected override _stream: CustomStream | undefined;
protected override _transcribe: CustomTranscribe | undefined;
protected override _agents: CustomAgents | undefined;

private readonly _encodeHeadersAsWsProtocols: boolean | undefined;

Expand Down Expand Up @@ -82,4 +86,21 @@ export class CortiClient extends BaseCortiClient {
encodeHeadersAsWsProtocols: this._encodeHeadersAsWsProtocols,
}));
}

public override get agents(): CustomAgents {
return (this._agents ??= new CustomAgents(this._options));
}

public getAuthHeaders = async (): Promise<Headers> => {
const req = await this._options.authProvider.getAuthRequest();

return new Headers({
...(req.headers ?? {}),
"Tenant-Name": await core.Supplier.get(this._options.tenantName),
});
};

/**
* Patch: removed `auth` getter
*/
Comment thread
tve-corti marked this conversation as resolved.
Outdated
}
26 changes: 26 additions & 0 deletions src/custom/agents/CustomAgents.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/**
* This file is the custom implementation of the Agents client (src/api/resources/agents/client/Client.ts)
*
* It extends the auto-generated Agents class and adds custom helper methods.
*
* All the patches marked with `// Patch: ...` comments.
*/

import { AgentsClient } from "../../api/resources/agents/client/Client.js";
import * as core from "../../core/index.js";

export class CustomAgents extends AgentsClient {
/**
* Patch: added helper method to get agent card URL
Comment thread
markitosha marked this conversation as resolved.
Outdated
*
* Returns the URL for the agent card JSON file.
*
* @param {string} agentId - The ID of the agentW
* @returns {Promise<URL>} The URL for the agent card
*
* @example
* const url = await client.agents.getAgentCardUrl("agent-123");
Comment thread
tve-corti marked this conversation as resolved.
Outdated
*/
public getCardUrl = async (agentId: string): Promise<URL> =>
new URL(`/agents/${agentId}/agent-card.json`, (await core.Supplier.get(this._options.environment)).agents);
Comment thread
tve-corti marked this conversation as resolved.
Outdated
}
126 changes: 126 additions & 0 deletions tests/custom/agents.getCardUrl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
import { CortiClient } from "../../src/custom/CortiClient";

describe("cortiClient.agents.getCardUrl", () => {
describe("with client credentials", () => {
it("should return correct URL for agent card", async () => {
const agentId = "test-agent-123";
const mockTenantName = "test-tenant";

const cortiClient = new CortiClient({
environment: "eu",
tenantName: mockTenantName,
auth: {
clientId: "test-client-id",
clientSecret: "test-client-secret",
},
});

const url = await cortiClient.agents.getCardUrl(agentId);

expect(url).toBeInstanceOf(URL);
expect(url.toString()).toBe(`https://api.eu.corti.app/agents/${agentId}/agent-card.json`);
});

it("should handle different agent IDs correctly", async () => {
const agentIds = ["agent-1", "550e8400-e29b-41d4-a716-446655440000", "my-custom-agent"];

const cortiClient = new CortiClient({
environment: "us",
tenantName: "prod-tenant",
auth: {
clientId: "test-client-id",
clientSecret: "test-client-secret",
},
});

for (const agentId of agentIds) {
const url = await cortiClient.agents.getCardUrl(agentId);

expect(url).toBeInstanceOf(URL);
expect(url.toString()).toBe(`https://api.us.corti.app/agents/${agentId}/agent-card.json`);
}
});

it("should work with dev environment", async () => {
const agentId = "test-agent";

const cortiClient = new CortiClient({
environment: "dev-eu",
tenantName: "dev-tenant",
auth: {
clientId: "test-client-id",
clientSecret: "test-client-secret",
},
});

const url = await cortiClient.agents.getCardUrl(agentId);

expect(url).toBeInstanceOf(URL);
expect(url.toString()).toBe(`https://api.dev-eu.corti.app/agents/${agentId}/agent-card.json`);
});
});

describe("with bearer token", () => {
it("should return correct URL for agent card", async () => {
const agentId = "bearer-agent-456";

// Helper function to create a mock JWT
const createMockJWT = (environment: string, tenant: string, expiresInSeconds: number = 3600): string => {
const header = { alg: "RS256", typ: "JWT" };
const payload = {
iss: `https://keycloak.${environment}.corti.app/realms/${tenant}`,
exp: Math.floor(Date.now() / 1000) + expiresInSeconds,
iat: Math.floor(Date.now() / 1000),
};

const base64UrlEncode = (str: string) => {
return Buffer.from(str)
.toString("base64")
.replace(/\+/g, "-")
.replace(/\//g, "_")
.replace(/=/g, "");
};

const encodedHeader = base64UrlEncode(JSON.stringify(header));
const encodedPayload = base64UrlEncode(JSON.stringify(payload));
const signature = "mock-signature";

return `${encodedHeader}.${encodedPayload}.${signature}`;
};

const mockAccessToken = createMockJWT("eu", "test-tenant");

const cortiClient = new CortiClient({
auth: {
accessToken: mockAccessToken,
refreshToken: "mock-refresh-token",
},
});

const url = await cortiClient.agents.getCardUrl(agentId);

expect(url).toBeInstanceOf(URL);
expect(url.toString()).toContain(`/agents/${agentId}/agent-card.json`);
});
});

describe("URL encoding", () => {
it("should handle special characters in agent ID", async () => {
const agentId = "agent with spaces";
const cortiClient = new CortiClient({
environment: "eu",
tenantName: "test-tenant",
auth: {
clientId: "test-client-id",
clientSecret: "test-client-secret",
},
});

const url = await cortiClient.agents.getCardUrl(agentId);

expect(url).toBeInstanceOf(URL);
// The URL constructor should handle encoding
expect(url.pathname).toContain("agent-card.json");
});
Comment thread
tve-corti marked this conversation as resolved.
Outdated
});
});
Loading
Loading