Skip to content

Commit 7c1ec12

Browse files
committed
feat: add user service
1 parent f7b0f2b commit 7c1ec12

File tree

9 files changed

+74
-83
lines changed

9 files changed

+74
-83
lines changed

packages/agent-webapp/index.html

Lines changed: 6 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,9 @@
1313
<img src="/favicon.png" alt="" />
1414
Contoso Burgers AI Agent
1515
<div class="spacer"></div>
16+
<azc-auth type="guard">
17+
<a href="">Member card</a>
18+
</azc-auth>
1619
<azc-auth type="status"></azc-auth>
1720
</nav>
1821
<main>
@@ -22,23 +25,9 @@
2225
</azc-auth>
2326
</main>
2427
<script type="module" src="/src/index.ts"></script>
25-
<script>
26-
// TODO: use userId from API
27-
// Generate a unique ID and store it in local storage
28-
const userId = localStorage.getItem('userId') || crypto.randomUUID();
29-
localStorage.setItem('userId', userId);
30-
31-
window.chatHistory.userId = userId;
32-
window.chatHistory.addEventListener('loadSession', (e) => {
33-
const { id, messages } = e.detail;
34-
window.chat.sessionId = id;
35-
window.chat.messages = messages;
36-
});
37-
38-
window.chat.userId = userId;
39-
window.chat.addEventListener('messagesUpdated', () => {
40-
window.chatHistory.refresh();
41-
});
28+
<script type="module">
29+
import { initUserSession } from '/src/index.ts';
30+
await initUserSession();
4231
</script>
4332
</body>
4433
</html>

packages/agent-webapp/src/api.ts renamed to packages/agent-webapp/src/api.service.ts

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,3 @@ export async function* getCompletion(options: ChatRequestOptions) {
2626
});
2727
}
2828
}
29-
30-
export function getCitationUrl(citation: string): string {
31-
return `${apiBaseUrl}/api/documents/${citation}`;
32-
}

packages/agent-webapp/src/components/auth.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ export const authDefaultOptions: AuthComponentOptions = {
7979
],
8080
};
8181

82-
export type AuthButtonType = 'status' | 'login' | 'logout';
82+
export type AuthButtonType = 'status' | 'login' | 'logout' | 'guard';
8383

8484
@customElement('azc-auth')
8585
export class AuthComponent extends LitElement {
@@ -131,6 +131,8 @@ export class AuthComponent extends LitElement {
131131
: nothing}
132132
</section>`;
133133

134+
protected renderGuard = () => (Boolean(this.loaded) ? html`<slot></slot>` : nothing);
135+
134136
protected renderLogin = () =>
135137
!this.loaded
136138
? html`<slot name="loader"></slot>`
@@ -177,6 +179,8 @@ export class AuthComponent extends LitElement {
177179
switch (this.type) {
178180
case 'status':
179181
return this.renderStatus();
182+
case 'guard':
183+
return this.renderGuard();
180184
case 'logout':
181185
return this.renderLogout();
182186
default:

packages/agent-webapp/src/components/card.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,22 @@ export const apiBaseUrl: string =
1010

1111
@customElement("register-user")
1212
export class RegisterUser extends LitElement {
13-
@state() protected accessToken: string = "";
13+
@state() protected userId: string = "";
1414
@state() protected isLoading = false;
1515
@state() protected hasError = false;
1616
@state() protected username: string = "";
1717

1818
constructor() {
1919
super();
20-
this.getAccessToken();
20+
this.getUserId();
2121
}
2222

2323
protected renderLoading = () => html`<p>Loading...</p>`;
2424

2525
protected copyUserIdToClipboard = async () => {
26-
if (this.accessToken) {
26+
if (this.userId) {
2727
try {
28-
await navigator.clipboard.writeText(this.accessToken);
28+
await navigator.clipboard.writeText(this.userId);
2929
// Select the user-id text
3030
const pre = this.renderRoot.querySelector(".user-id");
3131
if (pre) {
@@ -61,7 +61,7 @@ export class RegisterUser extends LitElement {
6161
<div><pre>${this.username}</pre></div>
6262
<p>Unique user ID:</p>
6363
<div class="user-id-row">
64-
<pre class="user-id">${this.accessToken}</pre>
64+
<pre class="user-id">${this.userId}</pre>
6565
<button
6666
class="copy-button"
6767
@click="${this.copyUserIdToClipboard}"
@@ -79,22 +79,22 @@ export class RegisterUser extends LitElement {
7979
</div>
8080
`;
8181

82-
protected getAccessToken = async () => {
82+
protected getUserId = async () => {
8383
this.isLoading = true;
8484
this.hasError = false;
8585
try {
8686
const authDetails = await getUserInfo();
8787
if (!authDetails) return;
8888
this.username = authDetails.userDetails;
8989

90-
const response = await fetch(`${apiBaseUrl}/api/me/access-token`);
90+
const response = await fetch(`${apiBaseUrl}/me`);
9191
if (!response.ok) {
92-
throw new Error("An error occurred while fetching the access token");
92+
throw new Error("An error occurred while fetching the user ID");
9393
}
9494
const data = await response.json();
95-
this.accessToken = data.accessToken;
95+
this.userId = data.userId;
9696
} catch (error) {
97-
console.error("Error fetching access token:", error);
97+
console.error("Error fetching user ID:", error);
9898
this.hasError = true;
9999
} finally {
100100
this.isLoading = false;
@@ -111,7 +111,7 @@ export class RegisterUser extends LitElement {
111111
: this.renderRegistrationCard();
112112
}
113113

114-
static styles = css`
114+
static override styles = css`
115115
:host {
116116
max-width: 1280px;
117117
margin: 0 auto;

packages/agent-webapp/src/components/chat.ts

Lines changed: 2 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { unsafeSVG } from 'lit/directives/unsafe-svg.js';
55
import { customElement, property, state, query } from 'lit/decorators.js';
66
import { AIChatCompletionDelta, AIChatMessage } from '@microsoft/ai-chat-protocol';
77
import { DebugComponent } from './debug.js';
8-
import { type ChatRequestOptions, getCitationUrl, getCompletion } from '../api.js';
8+
import { type ChatRequestOptions, getCompletion } from '../api.service.js';
99
import { type ParsedMessage, parseMessageIntoHtml } from '../message-parser.js';
1010
import sendSvg from '../../assets/icons/send.svg?raw';
1111
import questionSvg from '../../assets/icons/question.svg?raw';
@@ -258,14 +258,6 @@ export class ChatComponent extends LitElement {
258258
<azc-debug .message=${message}></azc-debug>
259259
</div>
260260
<div class="content">${message.html}</div>
261-
${message.citations.length > 0
262-
? html`
263-
<div class="citations">
264-
<div class="citations-title">${this.options.strings.citationsTitle}</div>
265-
${map(message.citations, this.renderCitation)}
266-
</div>
267-
`
268-
: nothing}
269261
</div>
270262
<div class="message-role">
271263
${message.role === 'user' ? this.options.strings.user : this.options.strings.assistant}
@@ -282,18 +274,6 @@ export class ChatComponent extends LitElement {
282274
</div>
283275
`;
284276

285-
protected renderCitation = (citation: string, index: number) =>
286-
html`<button
287-
class="citation"
288-
@click=${() => {
289-
this.onCitationClicked(citation);
290-
}}
291-
>
292-
${index + 1}. ${citation}
293-
</button>`;
294-
295-
protected renderCitationReference = (_citation: string, index: number) => html`<sup>[${index}]</sup>`;
296-
297277
protected renderFollowupQuestions = (questions: string[]) =>
298278
questions.length > 0
299279
? html`
@@ -354,7 +334,7 @@ export class ChatComponent extends LitElement {
354334
`;
355335

356336
protected override render() {
357-
const parsedMessages = this.messages.map((message) => parseMessageIntoHtml(message, this.renderCitationReference));
337+
const parsedMessages = this.messages.map((message) => parseMessageIntoHtml(message));
358338
return html`
359339
<section class="chat-container">
360340
${this.options.enablePromptSuggestions &&

packages/agent-webapp/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
export * from './api.js';
1+
export * from './api.service.js';
2+
export * from './user.service.js';
23
export * from './components/auth.js';
34
export * from './components/chat.js';
45
export * from './components/debug.js';

packages/agent-webapp/src/message-parser.ts

Lines changed: 3 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type HTMLTemplateResult, html, nothing } from 'lit';
1+
import { type HTMLTemplateResult, html } from 'lit';
22
import { AIChatMessage } from '@microsoft/ai-chat-protocol';
33

44
export type ParsedMessage = {
@@ -9,10 +9,7 @@ export type ParsedMessage = {
99
context?: object;
1010
};
1111

12-
export function parseMessageIntoHtml(
13-
message: AIChatMessage,
14-
renderCitationReference: (citation: string, index: number) => HTMLTemplateResult,
15-
): ParsedMessage {
12+
export function parseMessageIntoHtml(message: AIChatMessage): ParsedMessage {
1613
if (message.role === 'user') {
1714
return {
1815
html: html`${message.content}`,
@@ -36,27 +33,7 @@ export function parseMessageIntoHtml(
3633
.trim();
3734

3835
// Extract any citations that might be in the message
39-
const parts = text.split(/\[([^\]]+)]/g);
40-
const result = html`${parts.map((part, index) => {
41-
if (index % 2 === 0) {
42-
return html`${part}`;
43-
}
44-
45-
if (index + 1 < parts.length) {
46-
// Handle only completed citations
47-
let citationIndex = citations.indexOf(part);
48-
if (citationIndex === -1) {
49-
citations.push(part);
50-
citationIndex = citations.length;
51-
} else {
52-
citationIndex++;
53-
}
54-
55-
return renderCitationReference(part, citationIndex);
56-
}
57-
58-
return nothing;
59-
})}`;
36+
const result = html`${text}`;
6037

6138
return {
6239
html: result,

packages/agent-webapp/src/styles.css

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ azc-chat {
6565
margin: 0 auto;
6666
}
6767

68-
azc-auth[type='status'] {
68+
azc-auth[type] {
6969
font-size: 16px;
70+
margin-left: 1em;
7071
}
7172

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
declare global {
2+
interface Window {
3+
chatHistory: any;
4+
chat: any;
5+
}
6+
}
7+
8+
let userId: string | undefined;
9+
10+
export async function getUserId(refresh = false): Promise<string | undefined> {
11+
if (userId && !refresh) {
12+
return userId;
13+
}
14+
const response = await fetch(`/api/me`);
15+
const payload = await response.json();
16+
userId = payload?.id;
17+
return userId;
18+
}
19+
20+
export async function initUserSession() {
21+
try {
22+
const userId = await getUserId();
23+
if (!userId) {
24+
throw new Error('User not authenticated');
25+
}
26+
27+
// Set up user ID for chat history and chat components
28+
window.chatHistory.userId = userId;
29+
window.chatHistory.addEventListener('loadSession', (e) => {
30+
const { id, messages } = e.detail;
31+
window.chat.sessionId = id;
32+
window.chat.messages = messages;
33+
});
34+
35+
window.chat.userId = userId;
36+
window.chat.addEventListener('messagesUpdated', () => {
37+
window.chatHistory.refresh();
38+
});
39+
40+
} catch (error) {
41+
console.log('Error initializing user session:', error);
42+
}
43+
}

0 commit comments

Comments
 (0)