feat: Integrate backend list agents endpoint

Backend team successfully fixed Swagger conflicts and implemented simple GET
endpoints. Frontend now integrates with GET /api/agents to list all agents.

Changes:
- agent_endpoint.dart:
  - Added fromInt() methods to all enums (AgentType, AgentStatus, ModelProviderType)
  - Updated AgentDto.fromJson() to handle integer enum values from backend
  - Added listAgents() method using HTTP GET /api/agents
  - Added imports: dart:async, dart:convert, dart:io, package:http

- agents_page.dart:
  - Updated _loadAgents() to call listAgents() API method
  - Removed placeholder delay, now uses real data from backend
  - Removed unused getwidget import

Backend Integration:
 Backend returns 5 test agents (seeded successfully)
 Enums transmitted as integers (CodeGenerator=0, Active=0, etc.)
 Frontend properly parses integer enums to Dart enum types
 GET /api/agents endpoint working and tested
 Full CRUD cycle now functional

Testing:
- Flutter analyze: 0 errors, 0 warnings
- Backend health check:  passing
- List endpoint:  returns 5 agents
- App running: http://localhost:8080

Phase 2 Complete: Frontend can now display agents from backend!

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
jean-philippe 2025-10-26 20:28:45 -04:00
parent f5a5c5697c
commit 30575c6f77
2 changed files with 135 additions and 11 deletions

View File

@ -1,6 +1,12 @@
/// Agent management endpoints for CQRS API
library;
import 'dart:async';
import 'dart:convert';
import 'dart:io';
import 'package:http/http.dart' as http;
import '../client.dart';
import '../types.dart';
@ -25,6 +31,15 @@ enum AgentType {
orElse: () => AgentType.custom,
);
}
/// Convert from integer value (backend enum representation)
/// Backend: CodeGenerator=0, CodeReviewer=1, Debugger=2, Documenter=3, Custom=4
static AgentType fromInt(int value) {
if (value >= 0 && value < AgentType.values.length) {
return AgentType.values[value];
}
return AgentType.custom;
}
}
/// Represents the current status of an agent
@ -42,6 +57,15 @@ enum AgentStatus {
orElse: () => AgentStatus.inactive,
);
}
/// Convert from integer value (backend enum representation)
/// Backend: Active=0, Inactive=1, Error=2
static AgentStatus fromInt(int value) {
if (value >= 0 && value < AgentStatus.values.length) {
return AgentStatus.values[value];
}
return AgentStatus.inactive;
}
}
/// Specifies the type of model provider (cloud API or local endpoint)
@ -59,6 +83,15 @@ enum ModelProviderType {
orElse: () => ModelProviderType.custom,
);
}
/// Convert from integer value (backend enum representation)
/// Backend: CloudApi=0, LocalEndpoint=1, Custom=2
static ModelProviderType fromInt(int value) {
if (value >= 0 && value < ModelProviderType.values.length) {
return ModelProviderType.values[value];
}
return ModelProviderType.custom;
}
}
// =============================================================================
@ -239,21 +272,40 @@ class AgentDto {
});
factory AgentDto.fromJson(Map<String, Object?> json) {
// Helper to parse enum from either int or string
AgentType parseAgentType(Object? value) {
if (value is int) return AgentType.fromInt(value);
if (value is String) return AgentType.fromString(value);
return AgentType.custom;
}
AgentStatus parseAgentStatus(Object? value) {
if (value is int) return AgentStatus.fromInt(value);
if (value is String) return AgentStatus.fromString(value);
return AgentStatus.inactive;
}
ModelProviderType parseProviderType(Object? value) {
if (value is int) return ModelProviderType.fromInt(value);
if (value is String) return ModelProviderType.fromString(value);
return ModelProviderType.custom;
}
return AgentDto(
id: json['id'] as String,
name: json['name'] as String,
description: json['description'] as String,
type: AgentType.fromString(json['type'] as String),
type: parseAgentType(json['type']),
modelProvider: json['modelProvider'] as String,
modelName: json['modelName'] as String,
providerType: ModelProviderType.fromString(json['providerType'] as String),
providerType: parseProviderType(json['providerType']),
modelEndpoint: json['modelEndpoint'] as String?,
temperature: (json['temperature'] as num).toDouble(),
maxTokens: json['maxTokens'] as int,
systemPrompt: json['systemPrompt'] as String,
enableMemory: json['enableMemory'] as bool,
conversationWindowSize: json['conversationWindowSize'] as int,
status: AgentStatus.fromString(json['status'] as String),
status: parseAgentStatus(json['status']),
createdAt: DateTime.parse(json['createdAt'] as String),
updatedAt: DateTime.parse(json['updatedAt'] as String),
);
@ -361,4 +413,65 @@ extension AgentEndpoint on CqrsApiClient {
fromJson: (json) => AgentDto.fromJson(json as Map<String, Object?>),
);
}
/// List all agents
///
/// Returns a list of all active agents from the backend.
/// Backend endpoint: GET /api/agents
///
/// Example:
/// ```dart
/// final result = await client.listAgents();
///
/// result.when(
/// success: (agents) => print('Found ${agents.length} agents'),
/// error: (error) => print('Error: ${error.message}'),
/// );
/// ```
Future<Result<List<AgentDto>>> listAgents() async {
try {
final Uri url = Uri.parse('${config.baseUrl}/api/agents');
final http.Response response = await http
.get(url, headers: config.defaultHeaders)
.timeout(config.timeout);
if (response.statusCode >= 200 && response.statusCode < 300) {
final Object? jsonData = jsonDecode(response.body);
if (jsonData is! List) {
return ApiError<List<AgentDto>>(ApiErrorInfo(
message: 'Expected array response, got ${jsonData.runtimeType}',
type: ApiErrorType.serialization,
));
}
final List<AgentDto> agents = jsonData
.map((item) => AgentDto.fromJson(item as Map<String, Object?>))
.toList();
return ApiSuccess<List<AgentDto>>(agents);
}
// Handle error responses
return ApiError<List<AgentDto>>(ApiErrorInfo(
message: 'Failed to load agents',
type: ApiErrorType.http,
statusCode: response.statusCode,
));
} on TimeoutException {
return ApiError<List<AgentDto>>(ApiErrorInfo(
message: 'Request timed out',
type: ApiErrorType.timeout,
));
} on SocketException {
return ApiError<List<AgentDto>>(ApiErrorInfo(
message: 'No internet connection',
type: ApiErrorType.network,
));
} catch (e) {
return ApiError<List<AgentDto>>(ApiErrorInfo(
message: 'Unexpected error: $e',
type: ApiErrorType.unknown,
));
}
}
}

View File

@ -1,7 +1,6 @@
import 'package:flutter/material.dart';
import 'package:iconsax/iconsax.dart';
import 'package:animate_do/animate_do.dart';
import 'package:getwidget/getwidget.dart';
import '../api/api.dart';
import '../dialogs/create_agent_dialog.dart';
@ -43,14 +42,26 @@ class _AgentsPageState extends State<AgentsPage> {
_errorMessage = null;
});
// TODO: Implement list agents endpoint when available
// For now, show empty state
await Future<void>.delayed(const Duration(milliseconds: 500));
final Result<List<AgentDto>> result = await _apiClient.listAgents();
setState(() {
_agents = [];
_isLoading = false;
});
result.when(
success: (List<AgentDto> agents) {
if (mounted) {
setState(() {
_agents = agents;
_isLoading = false;
});
}
},
error: (ApiErrorInfo error) {
if (mounted) {
setState(() {
_errorMessage = error.message;
_isLoading = false;
});
}
},
);
}
Future<void> _createAgent(CreateAgentCommand command) async {