Skip to content

send backstage jwt token #4377

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

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
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
6 changes: 6 additions & 0 deletions workspaces/agent-forge/.changeset/good-ideas-shop.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'@backstage-community/plugin-agent-forge': minor
---

Retrieve the backstage auth token from IdentityApi and send it using the Auth Bearer header to each agent call.
Provide the agent card skill examples as options
1 change: 1 addition & 0 deletions workspaces/agent-forge/app-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ organization:

agentForge:
baseUrl: http://127.0.0.1:8000
showOptions: true

backend:
baseUrl: http://localhost:7007
Expand Down
9 changes: 9 additions & 0 deletions workspaces/agent-forge/plugins/agent-forge/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ agentForge:
...
```

The plugin can display as pre-defined options all the examples defined in the agent card skills. In order to do so, set the showOptions configuration to true:

```
...
agentForge:
showOptions: true
...
```

## Usage

Use the chat to ask your questions or perform the required task
Expand Down
5 changes: 5 additions & 0 deletions workspaces/agent-forge/plugins/agent-forge/config.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,10 @@ export interface Config {
* @visibility frontend
*/
baseUrl: string;

/**
* @visibility frontend
*/
showOptions: boolean;
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,6 @@ export class A2AClient {
);
}
const agentCard: AgentCard = await response.json();
console.log(agentCard);
if (!agentCard.url) {
throw new Error(
"Fetched Agent Card does not contain a valid 'url' for the service endpoint.",
Expand Down Expand Up @@ -166,7 +165,7 @@ export class A2AClient {
private async _postRpcRequest<
TParams,
TResponse extends JSONRPCResult<any> | JSONRPCErrorResponse,
>(method: string, params: TParams): Promise<TResponse> {
>(method: string, params: TParams, authToken?: string): Promise<TResponse> {
const endpoint = await this._getServiceEndpoint();
const requestId = this.requestIdCounter++;
const rpcRequest: JSONRPCRequest = {
Expand All @@ -179,6 +178,7 @@ export class A2AClient {
const httpResponse = await fetch(endpoint, {
method: 'POST',
headers: {
Authorization: 'Bearer ' + authToken,
'Content-Type': 'application/json',
Accept: 'application/json', // Expect JSON response for non-streaming requests
},
Expand Down Expand Up @@ -244,10 +244,12 @@ export class A2AClient {
*/
public async sendMessage(
params: MessageSendParams,
authToken?: string,
): Promise<SendMessageResponse> {
return this._postRpcRequest<MessageSendParams, SendMessageResponse>(
'message/send',
params,
authToken,
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,18 +21,26 @@ import {
SendMessageResponse,
Task,
TextPart,
AgentCard,
} from '../a2a/schema';
import { IdentityApi } from '@backstage/core-plugin-api';

export interface IChatbotApiOptions {}

export class ChatbotApi {
private client: A2AClient | null = null;
private contextId: string;
constructor(private apiBaseUrl: string, _?: IChatbotApiOptions) {
private identityApi: IdentityApi;
constructor(
private apiBaseUrl: string,
options: { identityApi: IdentityApi },
_?: IChatbotApiOptions,
) {
this.contextId = '';
if (!this.apiBaseUrl) {
throw new Error('Agent URL is not provided');
}
this.identityApi = options.identityApi;
try {
this.client = new A2AClient(this.apiBaseUrl);
} catch (error) {
Expand All @@ -42,8 +50,9 @@ export class ChatbotApi {

public async submitA2ATask(newContext: boolean, msg: string) {
try {
// Send a simple task (pass only params)
const msgId = uuidv4();
const { token } = await this.identityApi.getCredentials();

const sendParams: MessageSendParams = {
message: {
messageId: msgId,
Expand All @@ -57,7 +66,7 @@ export class ChatbotApi {
}
// Method now returns Task | null directly
const taskResult: SendMessageResponse | undefined =
await this.client?.sendMessage(sendParams);
await this.client?.sendMessage(sendParams, token);

const task: Task = taskResult?.result as Task;

Expand All @@ -80,4 +89,13 @@ export class ChatbotApi {
return 'Error connecting to agent';
}
}

public async getSkillExamples() {
const card: AgentCard | undefined = await this.client?.getAgentCard();
try {
return card?.skills[0].examples;
} catch (error) {
return [];
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import { Message, Feedback, UserResponse } from '../types';
import {
appThemeApiRef,
configApiRef,
identityApiRef,
useApi,
} from '@backstage/core-plugin-api';
import { createTimestamp, delay, makeLinksClickable } from '../utils';
Expand Down Expand Up @@ -75,9 +76,11 @@ function ChatAssistantApp() {
)}`,
);
}
const identityApi = useApi(identityApiRef);
const showOptions = config.getBoolean('agentForge.showOptions');

const chatbotApi = useMemo(
() => new ChatbotApi(backendUrl),
() => new ChatbotApi(backendUrl, { identityApi }),
// eslint-disable-next-line react-hooks/exhaustive-deps
[backendUrl],
);
Expand All @@ -93,6 +96,8 @@ function ChatAssistantApp() {
const [isPromptShown, setShowPrompt] = useState<boolean>(false);
const [isInitialState, setIsInitialState] = useState<boolean>(true);
const [isFullScreen, setIsFullScreen] = useState<boolean>(false);
const [suggestions, setSuggestions] = useState<string[]>([]);

const [providerModelsMap] = useState<{
[key: string]: string[];
}>({});
Expand Down Expand Up @@ -190,7 +195,8 @@ function ChatAssistantApp() {

await addUserMessage({ text: input, isUser: true });
const timestamp = createTimestamp();
switch (continueMessaging(input)) {
const contMsg = await continueMessaging(input);
switch (contMsg) {
case UserResponse.RESET:
// console.log('Reset Chat ID:', getChatId());
setIsTyping(false);
Expand Down Expand Up @@ -306,12 +312,14 @@ function ChatAssistantApp() {
]);
}

function continueMessaging(input = 'hi'): UserResponse {
async function continueMessaging(input = 'hi'): Promise<UserResponse> {
const [yesNo, ...additionalInput] = input.toLocaleLowerCase().split(' ');

if (additionalInput.length > 0) {
return UserResponse.CONTINUE;
}
const greetings = ['hi', 'hello', 'hey'];

switch (true) {
case greetings.some(greeting => yesNo.startsWith(greeting)):
return UserResponse.NEW;
Expand All @@ -321,6 +329,13 @@ function ChatAssistantApp() {
return UserResponse.CONTINUE;
}
}
if (showOptions && suggestions.length === 0) {
chatbotApi.getSkillExamples().then(value => {
if (value) {
setSuggestions(value);
}
});
}

if (!isOpen) {
return (
Expand Down Expand Up @@ -359,6 +374,7 @@ function ChatAssistantApp() {
<ChatTabs
isFullScreen={isFullScreen}
handleMessageSubmit={handleMessageSubmit}
suggestions={suggestions}
/>
) : (
<>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
* limitations under the License.
*/

/* eslint-disable react/react-in-jsx-scope*/

import logo from '../icons/jarvis.png';
import useStyles from './useStyles';
import Box from '@mui/material/Box';
Expand All @@ -23,10 +25,15 @@ import Button from '@mui/material/Button';
interface ChatTabsProps {
handleMessageSubmit: (msg?: string) => void;
isFullScreen?: boolean;
suggestions: string[] | [];
}
const suggestions: any[] = [];
// const suggestions: any[] = ["hello", "bello"];

function ChatTabs({ handleMessageSubmit, isFullScreen }: ChatTabsProps) {
function ChatTabs({
handleMessageSubmit,
isFullScreen,
suggestions,
}: ChatTabsProps) {
const handleTabClick = (message: string) => () => {
handleMessageSubmit(message);
};
Expand All @@ -51,7 +58,6 @@ function ChatTabs({ handleMessageSubmit, isFullScreen }: ChatTabsProps) {
</div>
<Grid
container
display="none"
maxHeight="70%"
marginBottom={10}
gap={2}
Expand Down Expand Up @@ -87,7 +93,17 @@ function ChatTabs({ handleMessageSubmit, isFullScreen }: ChatTabsProps) {
>
<div className={styles.greetingSection}>
<img className={styles.greetingLogo} src={logo} alt="Logo" />
<div className={styles.greetingText}>Hi there.</div>
</div>
<div className={styles.tabs}>
{suggestions.map(s => (
<Button
key={s}
className={styles.tabButton}
onClick={handleTabClick(s)}
>
{s}
</Button>
))}
</div>
</div>
);
Expand Down