mirror of
https://github.com/aljazceru/Auto-GPT.git
synced 2026-02-15 03:04:24 +01:00
Integrate Skill Tree Functionality into Main Application (#5189)
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/api_settings_viewmodel.dart';
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'views/main_layout.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
@@ -55,6 +56,9 @@ class MyApp extends StatelessWidget {
|
||||
create: (context) => ChatViewModel(chatService)),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => TaskViewModel(taskService)),
|
||||
ChangeNotifierProvider(
|
||||
create: (context) => SkillTreeViewModel(),
|
||||
),
|
||||
],
|
||||
child: MainLayout(),
|
||||
),
|
||||
|
||||
25
frontend/lib/models/skill_tree/ground.dart
Normal file
25
frontend/lib/models/skill_tree/ground.dart
Normal file
@@ -0,0 +1,25 @@
|
||||
class Ground {
|
||||
final String answer;
|
||||
final List<String> shouldContain;
|
||||
final List<String> shouldNotContain;
|
||||
final List<String> files;
|
||||
final Map<String, dynamic> eval;
|
||||
|
||||
Ground({
|
||||
required this.answer,
|
||||
required this.shouldContain,
|
||||
required this.shouldNotContain,
|
||||
required this.files,
|
||||
required this.eval,
|
||||
});
|
||||
|
||||
factory Ground.fromJson(Map<String, dynamic> json) {
|
||||
return Ground(
|
||||
answer: json['answer'],
|
||||
shouldContain: List<String>.from(json['should_contain']),
|
||||
shouldNotContain: List<String>.from(json['should_not_contain']),
|
||||
files: List<String>.from(json['files']),
|
||||
eval: json['eval'],
|
||||
);
|
||||
}
|
||||
}
|
||||
19
frontend/lib/models/skill_tree/info.dart
Normal file
19
frontend/lib/models/skill_tree/info.dart
Normal file
@@ -0,0 +1,19 @@
|
||||
class Info {
|
||||
final String difficulty;
|
||||
final String description;
|
||||
final List<String> sideEffects;
|
||||
|
||||
Info({
|
||||
required this.difficulty,
|
||||
required this.description,
|
||||
required this.sideEffects,
|
||||
});
|
||||
|
||||
factory Info.fromJson(Map<String, dynamic> json) {
|
||||
return Info(
|
||||
difficulty: json['difficulty'],
|
||||
description: json['description'],
|
||||
sideEffects: List<String>.from(json['side_effects']),
|
||||
);
|
||||
}
|
||||
}
|
||||
34
frontend/lib/models/skill_tree/skill_node_data.dart
Normal file
34
frontend/lib/models/skill_tree/skill_node_data.dart
Normal file
@@ -0,0 +1,34 @@
|
||||
import 'package:auto_gpt_flutter_client/models/skill_tree/ground.dart';
|
||||
import 'package:auto_gpt_flutter_client/models/skill_tree/info.dart';
|
||||
|
||||
class SkillNodeData {
|
||||
final String name;
|
||||
final List<String> category;
|
||||
final String task;
|
||||
final List<String> dependencies;
|
||||
final int cutoff;
|
||||
final Ground ground;
|
||||
final Info info;
|
||||
|
||||
SkillNodeData({
|
||||
required this.name,
|
||||
required this.category,
|
||||
required this.task,
|
||||
required this.dependencies,
|
||||
required this.cutoff,
|
||||
required this.ground,
|
||||
required this.info,
|
||||
});
|
||||
|
||||
factory SkillNodeData.fromJson(Map<String, dynamic> json) {
|
||||
return SkillNodeData(
|
||||
name: json['name'],
|
||||
category: List<String>.from(json['category']),
|
||||
task: json['task'],
|
||||
dependencies: List<String>.from(json['dependencies']),
|
||||
cutoff: json['cutoff'],
|
||||
ground: Ground.fromJson(json['ground']),
|
||||
info: Info.fromJson(json['info']),
|
||||
);
|
||||
}
|
||||
}
|
||||
23
frontend/lib/models/skill_tree/skill_tree_edge.dart
Normal file
23
frontend/lib/models/skill_tree/skill_tree_edge.dart
Normal file
@@ -0,0 +1,23 @@
|
||||
class SkillTreeEdge {
|
||||
final String id;
|
||||
final String from;
|
||||
final String to;
|
||||
final String arrows;
|
||||
|
||||
SkillTreeEdge({
|
||||
required this.id,
|
||||
required this.from,
|
||||
required this.to,
|
||||
required this.arrows,
|
||||
});
|
||||
|
||||
// Optionally, add a factory constructor to initialize from JSON
|
||||
factory SkillTreeEdge.fromJson(Map<String, dynamic> json) {
|
||||
return SkillTreeEdge(
|
||||
id: json['id'],
|
||||
from: json['from'],
|
||||
to: json['to'],
|
||||
arrows: json['arrows'],
|
||||
);
|
||||
}
|
||||
}
|
||||
18
frontend/lib/models/skill_tree/skill_tree_node.dart
Normal file
18
frontend/lib/models/skill_tree/skill_tree_node.dart
Normal file
@@ -0,0 +1,18 @@
|
||||
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_node_data.dart';
|
||||
|
||||
// TODO: Update this with actual data
|
||||
class SkillTreeNode {
|
||||
final String color;
|
||||
final int id;
|
||||
|
||||
// final SkillNodeData data;
|
||||
|
||||
SkillTreeNode({required this.color, required this.id});
|
||||
|
||||
factory SkillTreeNode.fromJson(Map<String, dynamic> json) {
|
||||
return SkillTreeNode(
|
||||
color: json['color'],
|
||||
id: json['id'],
|
||||
);
|
||||
}
|
||||
}
|
||||
76
frontend/lib/viewmodels/skill_tree_viewmodel.dart
Normal file
76
frontend/lib/viewmodels/skill_tree_viewmodel.dart
Normal file
@@ -0,0 +1,76 @@
|
||||
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_edge.dart';
|
||||
import 'package:auto_gpt_flutter_client/models/skill_tree/skill_tree_node.dart';
|
||||
import 'package:flutter/foundation.dart';
|
||||
import 'package:graphview/GraphView.dart';
|
||||
|
||||
class SkillTreeViewModel extends ChangeNotifier {
|
||||
List<SkillTreeNode> _skillTreeNodes = [];
|
||||
List<SkillTreeEdge> _skillTreeEdges = [];
|
||||
SkillTreeNode? _selectedNode;
|
||||
|
||||
SkillTreeNode? get selectedNode => _selectedNode;
|
||||
|
||||
final Graph graph = Graph()..isTree = true;
|
||||
BuchheimWalkerConfiguration builder = BuchheimWalkerConfiguration();
|
||||
|
||||
void initializeSkillTree() {
|
||||
_skillTreeNodes = [];
|
||||
_skillTreeEdges = [];
|
||||
|
||||
// Add nodes to _skillTreeNodes
|
||||
_skillTreeNodes.addAll([
|
||||
SkillTreeNode(color: 'red', id: 1),
|
||||
SkillTreeNode(color: 'blue', id: 2),
|
||||
SkillTreeNode(color: 'green', id: 3),
|
||||
SkillTreeNode(color: 'yellow', id: 4),
|
||||
SkillTreeNode(color: 'orange', id: 5),
|
||||
SkillTreeNode(color: 'purple', id: 6),
|
||||
SkillTreeNode(color: 'brown', id: 7),
|
||||
SkillTreeNode(color: 'pink', id: 8),
|
||||
SkillTreeNode(color: 'grey', id: 9),
|
||||
SkillTreeNode(color: 'cyan', id: 10),
|
||||
SkillTreeNode(color: 'magenta', id: 11),
|
||||
SkillTreeNode(color: 'lime', id: 12)
|
||||
]);
|
||||
|
||||
// Add edges to _skillTreeEdges
|
||||
_skillTreeEdges.addAll([
|
||||
SkillTreeEdge(id: '1_to_2', from: '1', to: '2', arrows: 'to'),
|
||||
SkillTreeEdge(id: '1_to_3', from: '1', to: '3', arrows: 'to'),
|
||||
SkillTreeEdge(id: '1_to_4', from: '1', to: '4', arrows: 'to'),
|
||||
SkillTreeEdge(id: '2_to_5', from: '2', to: '5', arrows: 'to'),
|
||||
SkillTreeEdge(id: '2_to_6', from: '2', to: '6', arrows: 'to'),
|
||||
SkillTreeEdge(id: '6_to_7', from: '6', to: '7', arrows: 'to'),
|
||||
SkillTreeEdge(id: '6_to_8', from: '6', to: '8', arrows: 'to'),
|
||||
SkillTreeEdge(id: '4_to_9', from: '4', to: '9', arrows: 'to'),
|
||||
SkillTreeEdge(id: '4_to_10', from: '4', to: '10', arrows: 'to'),
|
||||
SkillTreeEdge(id: '4_to_11', from: '4', to: '11', arrows: 'to'),
|
||||
SkillTreeEdge(id: '11_to_12', from: '11', to: '12', arrows: 'to')
|
||||
]);
|
||||
|
||||
builder
|
||||
..siblingSeparation = (100)
|
||||
..levelSeparation = (150)
|
||||
..subtreeSeparation = (150)
|
||||
..orientation = (BuchheimWalkerConfiguration.ORIENTATION_LEFT_RIGHT);
|
||||
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
void toggleNodeSelection(int nodeId) {
|
||||
if (_selectedNode?.id == nodeId) {
|
||||
// Unselect the node if it's already selected
|
||||
_selectedNode = null;
|
||||
} else {
|
||||
// Select the new node
|
||||
_selectedNode = _skillTreeNodes.firstWhere((node) => node.id == nodeId);
|
||||
}
|
||||
notifyListeners();
|
||||
}
|
||||
|
||||
// Getter to expose nodes for the View
|
||||
List<SkillTreeNode> get skillTreeNodes => _skillTreeNodes;
|
||||
|
||||
// Getter to expose edges for the View
|
||||
List<SkillTreeEdge> get skillTreeEdges => _skillTreeEdges;
|
||||
}
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/task_viewmodel.dart';
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/chat_viewmodel.dart';
|
||||
import 'package:auto_gpt_flutter_client/views/side_bar/side_bar_view.dart';
|
||||
@@ -23,6 +24,9 @@ class MainLayout extends StatelessWidget {
|
||||
// Access the ChatViewModel from the context
|
||||
final chatViewModel = Provider.of<ChatViewModel>(context);
|
||||
|
||||
// Access the ChatViewModel from the context
|
||||
final skillTreeViewModel = Provider.of<SkillTreeViewModel>(context);
|
||||
|
||||
// Check the screen width and return the appropriate layout
|
||||
if (width > 800) {
|
||||
// For larger screens, return a side-by-side layout
|
||||
@@ -36,7 +40,8 @@ class MainLayout extends StatelessWidget {
|
||||
return SizedBox(
|
||||
width: 280, child: TaskView(viewModel: taskViewModel));
|
||||
} else {
|
||||
return const Expanded(child: SkillTreeView());
|
||||
return Expanded(
|
||||
child: SkillTreeView(viewModel: skillTreeViewModel));
|
||||
}
|
||||
},
|
||||
),
|
||||
|
||||
@@ -1,20 +1,69 @@
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
|
||||
import 'package:auto_gpt_flutter_client/views/skill_tree/tree_node_view.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:graphview/GraphView.dart';
|
||||
|
||||
class SkillTreeView extends StatelessWidget {
|
||||
const SkillTreeView({super.key});
|
||||
class SkillTreeView extends StatefulWidget {
|
||||
final SkillTreeViewModel viewModel;
|
||||
|
||||
const SkillTreeView({Key? key, required this.viewModel}) : super(key: key);
|
||||
|
||||
@override
|
||||
_TreeViewPageState createState() => _TreeViewPageState();
|
||||
}
|
||||
|
||||
class _TreeViewPageState extends State<SkillTreeView> {
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
|
||||
widget.viewModel.initializeSkillTree();
|
||||
|
||||
// Create Node and Edge objects for GraphView
|
||||
final Map<int, Node> nodeMap = {};
|
||||
for (var skillTreeNode in widget.viewModel.skillTreeNodes) {
|
||||
final node = Node.Id(skillTreeNode.id);
|
||||
widget.viewModel.graph.addNode(node);
|
||||
nodeMap[skillTreeNode.id] = node;
|
||||
}
|
||||
|
||||
for (var skillTreeEdge in widget.viewModel.skillTreeEdges) {
|
||||
final fromNode = nodeMap[int.parse(skillTreeEdge.from)];
|
||||
final toNode = nodeMap[int.parse(skillTreeEdge.to)];
|
||||
widget.viewModel.graph.addEdge(fromNode!, toNode!);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return Container(
|
||||
color: Colors.blue[100], // Background color
|
||||
child: const Center(
|
||||
child: Text(
|
||||
'SkillTreeView',
|
||||
style: TextStyle(
|
||||
fontSize: 24,
|
||||
fontWeight: FontWeight.bold,
|
||||
return Scaffold(
|
||||
body: Column(
|
||||
mainAxisSize: MainAxisSize.max,
|
||||
children: [
|
||||
Expanded(
|
||||
child: InteractiveViewer(
|
||||
constrained: false,
|
||||
boundaryMargin: EdgeInsets.all(100),
|
||||
minScale: 0.01,
|
||||
maxScale: 5.6,
|
||||
child: GraphView(
|
||||
graph: widget.viewModel.graph,
|
||||
algorithm: BuchheimWalkerAlgorithm(widget.viewModel.builder,
|
||||
TreeEdgeRenderer(widget.viewModel.builder)),
|
||||
paint: Paint()
|
||||
..color = Colors.green
|
||||
..strokeWidth = 1
|
||||
..style = PaintingStyle.stroke,
|
||||
builder: (Node node) {
|
||||
int nodeId = node.key?.value as int;
|
||||
return TreeNodeView(
|
||||
nodeId: nodeId,
|
||||
selected: nodeId == widget.viewModel.selectedNode?.id);
|
||||
},
|
||||
),
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
32
frontend/lib/views/skill_tree/tree_node_view.dart
Normal file
32
frontend/lib/views/skill_tree/tree_node_view.dart
Normal file
@@ -0,0 +1,32 @@
|
||||
import 'package:auto_gpt_flutter_client/viewmodels/skill_tree_viewmodel.dart';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:provider/provider.dart';
|
||||
|
||||
class TreeNodeView extends StatelessWidget {
|
||||
final int nodeId;
|
||||
final bool selected;
|
||||
|
||||
TreeNodeView({required this.nodeId, this.selected = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return InkWell(
|
||||
onTap: () {
|
||||
print('Node $nodeId clicked');
|
||||
Provider.of<SkillTreeViewModel>(context, listen: false)
|
||||
.toggleNodeSelection(nodeId);
|
||||
},
|
||||
child: Container(
|
||||
padding: EdgeInsets.all(16),
|
||||
decoration: BoxDecoration(
|
||||
color: selected ? Colors.red : Colors.white,
|
||||
borderRadius: BorderRadius.circular(4),
|
||||
boxShadow: [
|
||||
BoxShadow(color: Colors.red, spreadRadius: 1),
|
||||
],
|
||||
),
|
||||
child: Text('Node $nodeId'),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -104,6 +104,14 @@ packages:
|
||||
description: flutter
|
||||
source: sdk
|
||||
version: "0.0.0"
|
||||
graphview:
|
||||
dependency: "direct main"
|
||||
description:
|
||||
name: graphview
|
||||
sha256: bdba183583b23c30c71edea09ad5f0beef612572d3e39e855467a925bd08392f
|
||||
url: "https://pub.dev"
|
||||
source: hosted
|
||||
version: "1.2.0"
|
||||
highlight:
|
||||
dependency: transitive
|
||||
description:
|
||||
|
||||
@@ -39,6 +39,7 @@ dependencies:
|
||||
provider: ^6.0.5
|
||||
http: ^1.1.0
|
||||
shared_preferences: ^2.2.1
|
||||
graphview: ^1.2.0
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
||||
Reference in New Issue
Block a user