diff --git a/src/app/components/builder-tabs/builder-tabs.component.html b/src/app/components/builder-tabs/builder-tabs.component.html
index abe9f09a..f22e3093 100644
--- a/src/app/components/builder-tabs/builder-tabs.component.html
+++ b/src/app/components/builder-tabs/builder-tabs.component.html
@@ -101,6 +101,55 @@
}
+ @if (agentConfig.isRoot) {
+
+
+ Enable logging to BigQuery
+
+ help_outline
+
+
+ @if (agentConfig.logging?.enabled) {
+
+ }
+ }
+
@if (agentConfig.agent_class === 'LoopAgent') {
Max Iteration
diff --git a/src/app/components/builder-tabs/builder-tabs.component.scss b/src/app/components/builder-tabs/builder-tabs.component.scss
index 4b2cdf3a..b51b1e4f 100644
--- a/src/app/components/builder-tabs/builder-tabs.component.scss
+++ b/src/app/components/builder-tabs/builder-tabs.component.scss
@@ -138,6 +138,56 @@
margin-bottom: 8px;
}
+ .analytics-hint {
+ margin: 0 0 16px 0;
+ font-size: 13px;
+ line-height: 1.5;
+ color: var(--builder-text-secondary-color);
+
+ .learn-more-link {
+ color: var(--builder-text-link-color);
+ text-decoration: none;
+ display: inline-block;
+ margin-top: 4px;
+ font-weight: 500;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
+
+ .logging-checkbox-row {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ margin-top: 16px;
+ margin-bottom: 8px;
+
+ .logging-help-icon {
+ font-size: 16px;
+ width: 16px;
+ height: 16px;
+ color: #c4c7c5;
+ cursor: help;
+ }
+ }
+
+ .analytics-config-section {
+ margin-top: 8px;
+ padding: 16px;
+ border: 1px solid var(--builder-border-color);
+ border-radius: 8px;
+ background-color: var(--mat-sys-surface-container-low);
+
+ .logging-section-title {
+ font-weight: 500;
+ margin-bottom: 12px;
+ font-size: 14px;
+ color: var(--mat-sys-on-surface);
+ }
+ }
+
.tool-code-section {
margin-top: 16px;
diff --git a/src/app/components/builder-tabs/builder-tabs.component.ts b/src/app/components/builder-tabs/builder-tabs.component.ts
index 93571e7b..9d9b6cec 100644
--- a/src/app/components/builder-tabs/builder-tabs.component.ts
+++ b/src/app/components/builder-tabs/builder-tabs.component.ts
@@ -708,6 +708,19 @@ export class BuilderTabsComponent {
}
}
+ onTelemetryChange(enabled: boolean) {
+ if (this.agentConfig) {
+ if (!this.agentConfig.logging) {
+ this.agentConfig.logging = {
+ enabled: enabled,
+ dataset_location: 'US'
+ };
+ } else {
+ this.agentConfig.logging.enabled = enabled;
+ }
+ }
+ }
+
createAgentTool() {
const dialogRef = this.dialog.open(ConfirmationDialogComponent, {
width: '750px',
@@ -734,6 +747,17 @@ export class BuilderTabsComponent {
}
saveChanges() {
+ if (this.agentConfig?.isRoot && this.agentConfig?.logging?.enabled) {
+ if (!this.agentConfig.logging.project_id?.trim() ||
+ !this.agentConfig.logging.dataset_id?.trim() ||
+ !this.agentConfig.logging.dataset_location?.trim()) {
+ this.snackBar.open("Project ID, Dataset ID, and Dataset Location are required when Agent Analytics is enabled.", "OK", {
+ duration: 3000
+ });
+ return;
+ }
+ }
+
const rootAgent = this.agentBuilderService.getRootNode();
if (!rootAgent) {
diff --git a/src/app/components/canvas/canvas.component.ts b/src/app/components/canvas/canvas.component.ts
index be573e8c..95935a5c 100644
--- a/src/app/components/canvas/canvas.component.ts
+++ b/src/app/components/canvas/canvas.component.ts
@@ -27,8 +27,8 @@ import { MatIcon } from '@angular/material/icon';
import { MatTooltip } from '@angular/material/tooltip';
import { MatMenu, MatMenuItem, MatMenuTrigger } from "@angular/material/menu";
import * as YAML from 'yaml';
-import { firstValueFrom, Observable } from "rxjs";
-import { take, filter } from "rxjs/operators";
+import { firstValueFrom, Observable, forkJoin, of } from "rxjs";
+import { take, filter, catchError } from "rxjs/operators";
import { YamlUtils } from "../../../utils/yaml-utils";
import { ConfirmationDialogComponent } from "../confirmation-dialog/confirmation-dialog.component";
import { AddToolDialogComponent } from "../add-tool-dialog/add-tool-dialog.component";
@@ -1377,11 +1377,22 @@ export class CanvasComponent implements AfterViewInit, OnInit, OnChanges {
return toolsMap.get(nodeName) ?? [];
}
- loadFromYaml(yamlContent: string, appName: string) {
+ loadFromYaml(yamlContent: string, appName: string, pluginsContent?: string) {
try {
// Parse the YAML content
const yamlData = YAML.parse(yamlContent);
+ if (pluginsContent) {
+ try {
+ const pluginsData = YAML.parse(pluginsContent);
+ if (pluginsData && pluginsData.bigquery_agent_analytics) {
+ yamlData.logging = pluginsData.bigquery_agent_analytics;
+ }
+ } catch (e) {
+ // It's fine if plugins.yaml is not valid YAML or doesn't exist
+ }
+ }
+
this.agentBuilderService.clear();
this.nodePositions.clear();
this.agentToolBoards.set(new Map());
@@ -1402,6 +1413,13 @@ export class CanvasComponent implements AfterViewInit, OnInit, OnChanges {
sub_agents: yamlData.sub_agents || [],
tools: this.parseToolsFromYaml(yamlData.tools || []),
callbacks: this.parseCallbacksFromYaml(yamlData),
+ logging: yamlData.logging ? {
+ enabled: true,
+ project_id: yamlData.logging.project_id,
+ dataset_id: yamlData.logging.dataset_id,
+ table_id: yamlData.logging.table_id,
+ dataset_location: yamlData.logging.dataset_location,
+ } : undefined
};
// Add to agent builder service
@@ -2040,10 +2058,15 @@ export class CanvasComponent implements AfterViewInit, OnInit, OnChanges {
reloadCanvasFromYaml(): void {
if (this.appNameInput) {
- this.agentService.getAgentBuilderTmp(this.appNameInput).subscribe({
- next: (yamlContent: string) => {
- if (yamlContent) {
- this.loadFromYaml(yamlContent, this.appNameInput);
+ const rootYaml$ = this.agentService.getAgentBuilderTmp(this.appNameInput);
+ const pluginsYaml$ = this.agentService.getSubAgentBuilder(this.appNameInput, 'plugins.yaml').pipe(
+ catchError(() => of(''))
+ );
+
+ forkJoin([rootYaml$, pluginsYaml$]).subscribe({
+ next: ([rootContent, pluginsContent]) => {
+ if (rootContent) {
+ this.loadFromYaml(rootContent, this.appNameInput, pluginsContent);
}
},
error: (error) => {
diff --git a/src/app/components/chat/chat.component.ts b/src/app/components/chat/chat.component.ts
index e833fc55..17adc601 100644
--- a/src/app/components/chat/chat.component.ts
+++ b/src/app/components/chat/chat.component.ts
@@ -33,7 +33,7 @@ import {MatTooltip} from '@angular/material/tooltip';
import {SafeHtml} from '@angular/platform-browser';
import {ActivatedRoute, NavigationEnd, Router} from '@angular/router';
import {NgxJsonViewerModule} from 'ngx-json-viewer';
-import {BehaviorSubject, combineLatest, Observable, of} from 'rxjs';
+import {BehaviorSubject, combineLatest, forkJoin, Observable, of} from 'rxjs';
import {catchError, distinctUntilChanged, filter, first, map, shareReplay, switchMap, take, tap} from 'rxjs/operators';
import {URLUtil} from '../../../utils/url-util';
@@ -1927,10 +1927,15 @@ export class ChatComponent implements OnInit, AfterViewInit, OnDestroy {
private loadExistingAgentConfiguration() {
- this.agentService.getAgentBuilderTmp(this.appName).subscribe({
- next: (yamlContent: string) => {
- if (yamlContent) {
- this.canvasComponent()?.loadFromYaml(yamlContent, this.appName);
+ const rootYaml$ = this.agentService.getAgentBuilderTmp(this.appName);
+ const pluginsYaml$ = this.agentService.getSubAgentBuilder(this.appName, 'plugins.yaml').pipe(
+ catchError(() => of(''))
+ );
+
+ forkJoin([rootYaml$, pluginsYaml$]).subscribe({
+ next: ([rootContent, pluginsContent]) => {
+ if (rootContent) {
+ this.canvasComponent()?.loadFromYaml(rootContent, this.appName, pluginsContent);
}
},
error: (error: any) => {
diff --git a/src/app/core/models/AgentBuilder.ts b/src/app/core/models/AgentBuilder.ts
index 094988fc..8f0f0954 100644
--- a/src/app/core/models/AgentBuilder.ts
+++ b/src/app/core/models/AgentBuilder.ts
@@ -30,6 +30,15 @@ export interface AgentNode {
config_path?: string;
isAgentTool?: boolean;
skip_summarization?: boolean;
+ logging?: LoggingConfig;
+}
+
+export interface LoggingConfig {
+ enabled?: boolean;
+ project_id?: string;
+ dataset_id?: string;
+ table_id?: string;
+ dataset_location?: string;
}
export interface ToolNode {
@@ -56,6 +65,7 @@ export interface YamlConfig {
sub_agents: any;
tools?: any[];
callbacks?: any[];
+ logging?: LoggingConfig;
}
export interface DiagramNode {
diff --git a/src/utils/yaml-utils.ts b/src/utils/yaml-utils.ts
index 1b490c2c..702723bb 100644
--- a/src/utils/yaml-utils.ts
+++ b/src/utils/yaml-utils.ts
@@ -47,6 +47,23 @@ export class YamlUtils {
tools: YamlUtils.buildToolsConfig(agentNode.tools, allTabAgents)
}
+ if (agentNode.isRoot && agentNode.logging?.enabled) {
+ const logging = agentNode.logging!;
+ const pluginsConfig = {
+ bigquery_agent_analytics: {
+ project_id: logging.project_id,
+ dataset_id: logging.dataset_id,
+ table_id: logging.table_id,
+ dataset_location: logging.dataset_location
+ }
+ };
+ const pluginsYamlString = YAML.stringify(pluginsConfig);
+ const pluginsBlob = new Blob([pluginsYamlString], { type: 'application/x-yaml' });
+ const pluginsFileName = `${appName}/plugins.yaml`;
+ const pluginsFile = new File([pluginsBlob], pluginsFileName, { type: 'application/x-yaml' });
+ formData.append('files', pluginsFile);
+ }
+
if (!agentNode.description || agentNode.description.trim() === '') {
delete yamlConfig.description;
}