-
Notifications
You must be signed in to change notification settings - Fork 94
initial port of dartantic_interface types #619
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
base: main
Are you sure you want to change the base?
Changes from 7 commits
4ad0ecd
86b9f84
2033059
9a41d31
dbeae4a
f301f5d
48b1401
ae9bf1b
c7cdf9a
4c6c8e2
a95f613
d2007d5
4f3b62f
3f1169d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1 +1,5 @@ | ||
| TODO: describe initial version here | ||
| # `genai_primitives` Changelog | ||
|
|
||
| ## 0.1.0 | ||
|
|
||
| - Initial release |
This file was deleted.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,116 @@ | ||
| import 'dart:convert'; | ||
| import 'dart:typed_data'; | ||
|
|
||
| import 'package:genai_primitives/genai_primitives.dart'; | ||
| import 'package:json_schema_builder/json_schema_builder.dart'; | ||
|
|
||
| void main() { | ||
| print('--- GenAI Primitives Example ---'); | ||
|
|
||
| // 1. Define a Tool | ||
| final ToolDefinition<Object> getWeatherTool = ToolDefinition( | ||
| name: 'get_weather', | ||
| description: 'Get the current weather for a location', | ||
| inputSchema: Schema.object( | ||
| properties: { | ||
| 'location': Schema.string( | ||
| description: 'The city and state, e.g. San Francisco, CA', | ||
| ), | ||
| 'unit': Schema.string( | ||
| enumValues: ['celsius', 'fahrenheit'], | ||
| description: 'The unit of temperature', | ||
| ), | ||
| }, | ||
| required: ['location'], | ||
| ), | ||
| ); | ||
|
|
||
| print('\n[Tool Definition]'); | ||
| print(const JsonEncoder.withIndent(' ').convert(getWeatherTool.toJson())); | ||
|
|
||
| // 2. Create a conversation history | ||
| final history = <ChatMessage>[ | ||
| // System message | ||
| ChatMessage.system( | ||
| 'You are a helpful weather assistant. ' | ||
| 'Use the get_weather tool when needed.', | ||
| ), | ||
|
|
||
| // User message asking for weather | ||
| ChatMessage.user('What is the weather in London?'), | ||
| ]; | ||
|
|
||
| print('\n[Initial Conversation]'); | ||
| for (final msg in history) { | ||
| print('${msg.role.name}: ${msg.text}'); | ||
| } | ||
|
|
||
| // 3. Simulate Model Response with Tool Call | ||
| final modelResponse = ChatMessage.model( | ||
| '', // Empty text for tool call | ||
| parts: [ | ||
| const TextPart('Thinking: User wants weather for London...'), | ||
| const ToolPart.call( | ||
| id: 'call_123', | ||
| name: 'get_weather', | ||
| arguments: {'location': 'London', 'unit': 'celsius'}, | ||
| ), | ||
| ], | ||
| ); | ||
| history.add(modelResponse); | ||
|
|
||
| print('\n[Model Response with Tool Call]'); | ||
| if (modelResponse.hasToolCalls) { | ||
| for (final ToolPart call in modelResponse.toolCalls) { | ||
| print('Tool Call: ${call.name}(${call.arguments})'); | ||
| } | ||
| } | ||
|
|
||
| // 4. Simulate Tool Execution & Result | ||
| final toolResult = ChatMessage.user( | ||
| '', // User role is typically used for tool results in many APIs | ||
| parts: [ | ||
| const ToolPart.result( | ||
| id: 'call_123', | ||
| name: 'get_weather', | ||
| result: {'temperature': 15, 'condition': 'Cloudy'}, | ||
| ), | ||
| ], | ||
| ); | ||
| history.add(toolResult); | ||
|
|
||
| print('\n[Tool Result]'); | ||
| print('Result: ${toolResult.toolResults.first.result}'); | ||
|
|
||
| // 5. Simulate Final Model Response with Data (e.g. an image generated or | ||
| // returned) | ||
| final finalResponse = ChatMessage.model( | ||
| 'Here is a chart of the weather trend:', | ||
| parts: [ | ||
| DataPart( | ||
| Uint8List.fromList([0x89, 0x50, 0x4E, 0x47]), // Fake PNG header | ||
| mimeType: 'image/png', | ||
| name: 'weather_chart.png', | ||
| ), | ||
| ], | ||
| ); | ||
| history.add(finalResponse); | ||
|
|
||
| print('\n[Final Model Response with Data]'); | ||
| print('Text: ${finalResponse.text}'); | ||
| if (finalResponse.parts.any((p) => p is DataPart)) { | ||
| final DataPart dataPart = finalResponse.parts.whereType<DataPart>().first; | ||
| print( | ||
| 'Attachment: ${dataPart.name} ' | ||
| '(${dataPart.mimeType}, ${dataPart.bytes.length} bytes)', | ||
| ); | ||
| } | ||
|
|
||
| // 6. Demonstrate JSON serialization of the whole history | ||
| print('\n[Full History JSON]'); | ||
| print( | ||
| const JsonEncoder.withIndent( | ||
| ' ', | ||
| ).convert(history.map((m) => m.toJson()).toList()), | ||
| ); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,125 @@ | ||
| import 'package:meta/meta.dart'; | ||
|
|
||
| import 'message_parts.dart'; | ||
| import 'utils.dart'; | ||
|
|
||
| /// A message in a conversation between a user and a model. | ||
| @immutable | ||
| class ChatMessage { | ||
| /// Creates a new message. | ||
| const ChatMessage({ | ||
| required this.role, | ||
| required this.parts, | ||
| this.metadata = const {}, | ||
| }); | ||
|
|
||
| /// Creates a message from a JSON-compatible map. | ||
| factory ChatMessage.fromJson(Map<String, dynamic> json) => ChatMessage( | ||
| role: ChatMessageRole.values.byName(json['role'] as String), | ||
| parts: (json['parts'] as List<dynamic>) | ||
| .map((p) => Part.fromJson(p as Map<String, dynamic>)) | ||
| .toList(), | ||
| metadata: json['metadata'] as Map<String, dynamic>, | ||
| ); | ||
|
|
||
| /// Creates a system message. | ||
| factory ChatMessage.system( | ||
| String text, { | ||
| List<Part> parts = const [], | ||
| Map<String, dynamic>? metadata, | ||
| }) => ChatMessage( | ||
| role: ChatMessageRole.system, | ||
| parts: [TextPart(text), ...parts], | ||
| metadata: metadata ?? const {}, | ||
| ); | ||
|
|
||
| /// Creates a user message with text. | ||
| factory ChatMessage.user( | ||
| String text, { | ||
| List<Part> parts = const [], | ||
| Map<String, dynamic>? metadata, | ||
| }) => ChatMessage( | ||
| role: ChatMessageRole.user, | ||
| parts: [TextPart(text), ...parts], | ||
| metadata: metadata ?? const {}, | ||
| ); | ||
|
|
||
| /// Creates a model message with text. | ||
| factory ChatMessage.model( | ||
| String text, { | ||
| List<Part> parts = const [], | ||
| Map<String, dynamic>? metadata, | ||
| }) => ChatMessage( | ||
| role: ChatMessageRole.model, | ||
| parts: [TextPart(text), ...parts], | ||
| metadata: metadata ?? const {}, | ||
| ); | ||
|
|
||
| /// The role of the message author. | ||
| final ChatMessageRole role; | ||
|
|
||
| /// The content parts of the message. | ||
| final List<Part> parts; | ||
|
|
||
| /// Optional metadata associated with this message. | ||
| /// Can include information like suppressed content, warnings, etc. | ||
| final Map<String, dynamic> metadata; | ||
|
|
||
| /// Gets the text content of the message by concatenating all text parts. | ||
| String get text => parts.whereType<TextPart>().map((p) => p.text).join(); | ||
|
|
||
| /// Checks if this message contains any tool calls. | ||
| bool get hasToolCalls => | ||
| parts.whereType<ToolPart>().any((p) => p.kind == ToolPartKind.call); | ||
|
|
||
| /// Gets all tool calls in this message. | ||
| List<ToolPart> get toolCalls => parts | ||
| .whereType<ToolPart>() | ||
| .where((p) => p.kind == ToolPartKind.call) | ||
| .toList(); | ||
|
|
||
| /// Checks if this message contains any tool results. | ||
| bool get hasToolResults => | ||
| parts.whereType<ToolPart>().any((p) => p.kind == ToolPartKind.result); | ||
|
|
||
| /// Gets all tool results in this message. | ||
| List<ToolPart> get toolResults => parts | ||
| .whereType<ToolPart>() | ||
| .where((p) => p.kind == ToolPartKind.result) | ||
| .toList(); | ||
|
|
||
| /// Converts the message to a JSON-compatible map. | ||
| Map<String, dynamic> toJson() => { | ||
| 'role': role.name, | ||
| 'parts': parts.map((p) => p.toJson()).toList(), | ||
| 'metadata': metadata, | ||
| }; | ||
|
|
||
| @override | ||
| bool operator ==(Object other) => | ||
| identical(this, other) || | ||
| other is ChatMessage && | ||
| runtimeType == other.runtimeType && | ||
| role == other.role && | ||
| listEquals(parts, other.parts) && | ||
| mapEquals(metadata, other.metadata); | ||
|
|
||
| @override | ||
| int get hashCode => role.hashCode ^ parts.hashCode ^ metadata.hashCode; | ||
csells marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| @override | ||
| String toString() => | ||
| 'Message(role: $role, parts: $parts, metadata: $metadata)'; | ||
| } | ||
|
|
||
| /// The role of a message author. | ||
| enum ChatMessageRole { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you add more details to doc strings for enum and its items? I can represent a confused user here:
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Maybe in this library instead of ChatMessage having field
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Another option: class ChatMessage<T>{
...
final T type;
...
}Then users will be able to pass their enums for the message type. Maybe some of them will have more than one model and more than one system.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Or, we can remove type completely, so that users just derive from ChatMessage and add whatever fields they want to add. Or users can compose ChatMessage into another object, that contains envelope information. This seems to be cleanest option for me. |
||
| /// A message from the system that sets context or instructions. | ||
| system, | ||
|
|
||
| /// A message from the end user. | ||
| user, | ||
|
|
||
| /// A message from the model. | ||
| model, | ||
| } | ||
This file was deleted.
Uh oh!
There was an error while loading. Please reload this page.