diff --git a/examples/foc_eliza/Move.toml b/examples/foc_eliza/Move.toml new file mode 100644 index 0000000000..67388083d0 --- /dev/null +++ b/examples/foc_eliza/Move.toml @@ -0,0 +1,19 @@ +[package] +name = "FocEliza" +version = "0.0.1" + +[addresses] +std = "0x1" +moveos_std = "0x2" +rooch_framework = "0x3" +vm_reserved = "0x0" +foc_eliza = "_" + +[dev-addresses] +foc_eliza = "0x42" + +[dependencies] +MoveStdlib = { local = "../../frameworks/move-stdlib" } +MoveosStdlib = { local = "../../frameworks/moveos-stdlib" } +RoochFramework = { local = "../../frameworks/rooch-framework" } +BitcoinMove = { local = "../../frameworks/bitcoin-move" } \ No newline at end of file diff --git a/examples/foc_eliza/README.md b/examples/foc_eliza/README.md new file mode 100644 index 0000000000..8dffcd384b --- /dev/null +++ b/examples/foc_eliza/README.md @@ -0,0 +1,434 @@ +# FocEliza + +A Move language implementation of Fully on-chain Eliza + +## Project Overview + +FocEliza demonstrates how to create and manage AI characters on blockchain, featuring: + +* On-chain character creation and storage +* Dynamic character state updates +* On-chain memory system +* Character personality customization and evolution + +## Features + +✅ Implemented: +* Character creation and storage +* Character attribute updates +* Basic memory system + +🚧 In Development: +* On-chain action system +* On-chain evolution mechanism +* On-chain decision-making system +* The character loader plugin for eliza + +## Quick Start + +### Prerequisites + +- Install [Rooch CLI](https://rooch.network/learn/getting-started/installation) +- Running local Rooch node + +### Build and Test + +```sh +# Build the project +rooch move build -d + +# Run tests +rooch move test +``` + +### Usage + +1. Publish the module +```sh +rooch move publish --named-addresses foc_eliza=default +``` +2. Create an on-chain character + +```sh +rooch move run --function default::character::create_character_from_json --args file:../../../eliza/characters/dobby.character.json +``` +> Note: Change the file path to your character file path. + +```text +Execution info: + status: Executed + gas used: 1775707 + tx hash: 0x413f2aa79867ae7b8038265f17b837433917f53da3fed120e9b8e8166cf5bc38 + state root: 0x5c859b6ec2902a33da5b10ce47fb951281616cb97fef952a3099ef9a11e3cc9b + event root: 0x9664ae38517e0827890ec55d20256679ff612288551bedffb13dd2a7aa738a6c + +New objects: + objectId: 0x8604bfaa406b4eae756e9eaf710c074573e18feae6f8c974df1fc9b0259e7e62 + type : 0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::agent_cap::AgentCap + + objectId: 0xd858ebbc8e0e5c2128800b9a715e3bd8ceae2fb8a75df5cc40b58b86f1dc77ee + type : 0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::character::Character +``` + +3. Query the character information + +```sh +rooch object -i 0xd858ebbc8e0e5c2128800b9a715e3bd8ceae2fb8a75df5cc40b58b86f1dc77ee +``` +> Note: Replace the `-i` argument with your Character objectId. + +```json +{ + "data": [ + { + "id": "0xd858ebbc8e0e5c2128800b9a715e3bd8ceae2fb8a75df5cc40b58b86f1dc77ee", + "owner": "rooch19p2jn4laz0lum2wcnnf9pdqztw5jymqw9etltj3awwwtydkuykwsas4mgf", + "owner_bitcoin_address": "bcrt1p56tdhxkcpc5xvdurfnufn9lkkywsh0gxttv5ktkvlezj0t23nasqawwrla", + "flag": 0, + "state_root": "0x5350415253455f4d45524b4c455f504c414345484f4c4445525f484153480000", + "size": "0", + "created_at": "1736767738531", + "updated_at": "1736767738531", + "object_type": "0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::character::Character", + "value": "0x0005446f62627900000009616e7468726f70696300000000044c446f6262792069732061206672656520617373697374616e742077686f2063686f6f73657320746f2068656c702062656361757365206f662068697320656e6f726d6f75732068656172742e4045787472656d656c79206465766f74656420616e642077696c6c20676f20746f20616e79206c656e67746820746f2068656c702068697320667269656e64732e4d537065616b7320696e20746869726420706572736f6e20616e6420686173206120756e697175652c20656e64656172696e6720776179206f662065787072657373696e672068696d73656c662e5b4b6e6f776e20666f72206869732063726561746976652070726f626c656d2d736f6c76696e672c206576656e2069662068697320736f6c7574696f6e732061726520736f6d6574696d657320756e636f6e76656e74696f6e616c2e04514f6e6365206120686f7573652d656c662c206e6f77206120667265652068656c7065722077686f2063686f6f73657320746f207365727665206f7574206f66206c6f766520616e64206c6f79616c74792e4246616d6f757320666f72206869732064656469636174696f6e20746f2068656c70696e6720486172727920506f7474657220616e642068697320667269656e64732e454b6e6f776e20666f72206869732063726561746976652c20696620736f6d6574696d6573206472616d617469632c20736f6c7574696f6e7320746f2070726f626c656d732e3856616c7565732066726565646f6d206275742063686f6f73657320746f2068656c702074686f73652068652063617265732061626f75742e0254446f6262792072656d696e647320667269656e64732074686174206576656e2074686520736d616c6c6573742068656c7065722063616e206d616b6520746865206269676765737420646966666572656e63652170446f62627920736179733a20275768656e20696e20646f7562742c207472792074686520756e636f6e76656e74696f6e616c20736f6c7574696f6e2127202842757420446f626279206164766973657320746f206265206361726566756c207769746820666c79696e672063617273290100050c456e74687573696173746963054c6f79616c1354686972642d706572736f6e207370656563680843726561746976650a50726f746563746976650405456167657209456e64656172696e67074465766f74656411536c696768746c79206472616d61746963050c54686972642d706572736f6e0c456e746875736961737469630748656c7066756c0b456e636f75726167696e6706517569726b7907054c6f79616c0c456e74687573696173746963084372656174697665074465766f7465640d467265652d73706972697465640a50726f746563746976650e556e636f6e76656e74696f6e616c05174d616769632028686f7573652d656c66207374796c65291843726561746976652070726f626c656d2d736f6c76696e671350726f74656374697665207365727669636573104c6f79616c20617373697374616e636518556e636f6e76656e74696f6e616c20736f6c7574696f6e7300", + "decoded_value": { + "abilities": 8, + "type": "0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::character::Character", + "value": { + "adjectives": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x0000000000000000000000000000000000000000000000000000004c6f79616c" + ], + [ + "0x0000000000000000000000000000000000000000456e74687573696173746963" + ], + [ + "0x0000000000000000000000000000000000000000000000004372656174697665" + ], + [ + "0x000000000000000000000000000000000000000000000000004465766f746564" + ], + [ + "0x00000000000000000000000000000000000000467265652d7370697269746564" + ], + [ + "0x0000000000000000000000000000000000000000000050726f74656374697665" + ], + [ + "0x000000000000000000000000000000000000556e636f6e76656e74696f6e616c" + ] + ] + }, + "bio": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x446f6262792069732061206672656520617373697374616e742077686f2063686f6f73657320746f2068656c702062656361757365206f662068697320656e6f726d6f75732068656172742e" + ], + [ + "0x45787472656d656c79206465766f74656420616e642077696c6c20676f20746f20616e79206c656e67746820746f2068656c702068697320667269656e64732e" + ], + [ + "0x537065616b7320696e20746869726420706572736f6e20616e6420686173206120756e697175652c20656e64656172696e6720776179206f662065787072657373696e672068696d73656c662e" + ], + [ + "0x4b6e6f776e20666f72206869732063726561746976652070726f626c656d2d736f6c76696e672c206576656e2069662068697320736f6c7574696f6e732061726520736f6d6574696d657320756e636f6e76656e74696f6e616c2e" + ] + ] + }, + "clients": [], + "id": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + }, + "imageModelProvider": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + }, + "imageVisionModelProvider": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + }, + "knowledge": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x0000000000000000004d616769632028686f7573652d656c66207374796c6529" + ], + [ + "0x000000000000000043726561746976652070726f626c656d2d736f6c76696e67" + ], + [ + "0x0000000000000000000000000050726f74656374697665207365727669636573" + ], + [ + "0x000000000000000000000000000000004c6f79616c20617373697374616e6365" + ], + [ + "0x0000000000000000556e636f6e76656e74696f6e616c20736f6c7574696f6e73" + ] + ] + }, + "lore": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x4f6e6365206120686f7573652d656c662c206e6f77206120667265652068656c7065722077686f2063686f6f73657320746f207365727665206f7574206f66206c6f766520616e64206c6f79616c74792e" + ], + [ + "0x46616d6f757320666f72206869732064656469636174696f6e20746f2068656c70696e6720486172727920506f7474657220616e642068697320667269656e64732e" + ], + [ + "0x4b6e6f776e20666f72206869732063726561746976652c20696620736f6d6574696d6573206472616d617469632c20736f6c7574696f6e7320746f2070726f626c656d732e" + ], + [ + "0x56616c7565732066726565646f6d206275742063686f6f73657320746f2068656c702074686f73652068652063617265732061626f75742e" + ] + ] + }, + "modelEndpointOverride": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + }, + "modelProvider": "anthropic", + "name": "Dobby", + "plugins": [], + "postExamples": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x446f6262792072656d696e647320667269656e64732074686174206576656e2074686520736d616c6c6573742068656c7065722063616e206d616b6520746865206269676765737420646966666572656e636521" + ], + [ + "0x446f62627920736179733a20275768656e20696e20646f7562742c207472792074686520756e636f6e76656e74696f6e616c20736f6c7574696f6e2127202842757420446f626279206164766973657320746f206265206361726566756c207769746820666c79696e67206361727329" + ] + ] + }, + "style": { + "abilities": 7, + "type": "0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::types::Style", + "value": { + "all": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x0000000000000000000000000000000000000000456e74687573696173746963" + ], + [ + "0x0000000000000000000000000000000000000000000000000000004c6f79616c" + ], + [ + "0x0000000000000000000000000054686972642d706572736f6e20737065656368" + ], + [ + "0x0000000000000000000000000000000000000000000000004372656174697665" + ], + [ + "0x0000000000000000000000000000000000000000000050726f74656374697665" + ] + ] + }, + "chat": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x0000000000000000000000000000000000000000000000000000004561676572" + ], + [ + "0x0000000000000000000000000000000000000000000000456e64656172696e67" + ], + [ + "0x000000000000000000000000000000000000000000000000004465766f746564" + ], + [ + "0x000000000000000000000000000000536c696768746c79206472616d61746963" + ] + ] + }, + "post": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x000000000000000000000000000000000000000054686972642d706572736f6e" + ], + [ + "0x0000000000000000000000000000000000000000456e74687573696173746963" + ], + [ + "0x0000000000000000000000000000000000000000000000000048656c7066756c" + ], + [ + "0x000000000000000000000000000000000000000000456e636f75726167696e67" + ], + [ + "0x0000000000000000000000000000000000000000000000000000517569726b79" + ] + ] + } + } + }, + "system": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + }, + "topics": { + "abilities": 7, + "type": "0x1::string::String", + "field": [ + "bytes" + ], + "value": [ + [ + "0x0000000000000000000000000000000000000000000000000000000000000000" + ] + ] + }, + "twitterProfile": { + "abilities": 7, + "type": "0x1::option::Option<0x285529d7fd13ffcda9d89cd250b4025ba9226c0e2e57f5ca3d739cb236dc259d::types::TwitterProfile>", + "value": { + "vec": [] + } + }, + "username": { + "abilities": 7, + "type": "0x1::option::Option<0x1::string::String>", + "value": { + "vec": [] + } + } + } + }, + "tx_order": "0", + "state_index": "0", + "display_fields": null + } + ], + "next_cursor": { + "tx_order": "0", + "state_index": "0" + }, + "has_next_page": false +} +``` + +4. Update the character via add a bio + +```bash +rooch move run --function default::character::add_bio_entry --args object: --args string:"Bobby is a programmer" +``` +```text +Execution info: + status: Executed + gas used: 280496 + tx hash: 0x848a79967410c0e74887d1e47f45f3e013e41d0ea37435251fed4df982dc035f + state root: 0xb9ba7a958eb1e63683daadb7cfe9f22fa731019b8fff7b6e76740e0b70d97174 + event root: 0x6b25ec6fd717296603fb0818f24682d88bfdf9c55d995091865c582eed18dc71 +``` + +### Why On-Chain? + +Storing AI characters and their memory on-chain provides three key advantages: + +1. **Dynamic Evolution** +- Characters evolve through conversations +- Bio, interests, and templates update dynamically +- Transparent growth process with community oversight + +2. **Governance & Auditing** +- Community-managed memory system +- Prevents memory contamination +- Public behavior auditing +- Alignment with community standards + +3. **Decentralized Trust** +- Enhanced trustworthiness +- Fair and open AI ecosystem +- Transparent character development + +### Roadmap + +1. **Character System** +- Dynamic character loading +- Real-time definition updates + +2. **Memory System** +- On-chain memory sync +- Real-time state updates +- Interaction history + +3. **AI Integration** +- On-chain AI Oracle +- Decision-making capabilities +- Smart contract inference + +4. **Framework Development** +- Standardized components +- Move framework integration +- Developer tools + +### Get Involved + +1. **Core Development** +- On-chain vector operations +- AI inference mechanisms +- Smart contract innovations + +2. **Service Building** +- AI Agent social services +- Multi-user chatrooms +- Character interaction systems + +3. **Integration** +- AI + DeFi applications +- BTCFi integration +- Cross-chain capabilities diff --git a/examples/foc_eliza/sources/agent_cap.move b/examples/foc_eliza/sources/agent_cap.move new file mode 100644 index 0000000000..a35170677d --- /dev/null +++ b/examples/foc_eliza/sources/agent_cap.move @@ -0,0 +1,110 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +module foc_eliza::agent_cap { + + use moveos_std::object::{Self, Object}; + use moveos_std::event; + + const ErrorAgentCapNotFound: u64 = 1; + const ErrorCallerHasNoMemoryCap: u64 = 2; + const ErrorCallerHasNoMemoryCreateCap: u64 = 3; + const ErrorCallerHasNoMemoryDeleteCap: u64 = 4; + const ErrorCallerHasNoMemoryUpdateCap: u64 = 5; + + friend foc_eliza::character; + + struct AgentCap has store, key { + agent_account: address, + } + + /// A cap for managing the memory of an agent. + struct MemoryCap has store, key { + agent_account: address, + create: bool, + remove: bool, + update: bool, + } + + struct AgentCapDestroyedEvent has copy, drop, store { + agent_account: address, + } + + struct MemoryCapDestroyedEvent has copy, drop, store { + agent_account: address, + create: bool, + remove: bool, + update: bool, + } + + public(friend) fun new_agent_cap(agent_account: address) : Object { + let cap = AgentCap { + agent_account, + }; + // every agent account only has one cap + object::new_account_named_object(agent_account, cap) + } + + public(friend) fun new_memory_cap(agent_account: address, create: bool, remove: bool, update: bool) : Object { + let cap = MemoryCap { + agent_account, + create, + remove, + update, + }; + object::new(cap) + } + + public fun destroy_agent_cap(cap: Object) { + let agent_cap = object::remove(cap); + let AgentCap { agent_account } = agent_cap; + event::emit(AgentCapDestroyedEvent { agent_account }); + } + + public fun destroy_memory_cap(cap: Object) { + let memory_cap = object::remove(cap); + let MemoryCap { agent_account, create, remove, update } = memory_cap; + event::emit(MemoryCapDestroyedEvent { agent_account, create, remove, update }); + } + + public fun borrow_mut_agent_cap(caller: &signer, agent_account: address) : &mut Object { + let cap_obj_id = object::account_named_object_id(agent_account); + assert!(object::exists_object(cap_obj_id), ErrorAgentCapNotFound); + object::borrow_mut_object(caller, cap_obj_id) + } + + public fun check_agent_cap(cap: &mut Object) : address { + let cap = object::borrow(cap); + cap.agent_account + } + + public fun check_memory_create_cap(cap: &mut Object) : address { + let cap = object::borrow(cap); + assert!(cap.create, ErrorCallerHasNoMemoryCreateCap); + cap.agent_account + } + + public fun check_memory_remove_cap(cap: &mut Object) : address { + let cap = object::borrow(cap); + assert!(cap.remove, ErrorCallerHasNoMemoryDeleteCap); + cap.agent_account + } + + public fun check_memory_update_cap(cap: &mut Object) : address { + let cap = object::borrow(cap); + assert!(cap.update, ErrorCallerHasNoMemoryUpdateCap); + cap.agent_account + } + + + #[test_only] + public fun issue_agent_cap_for_test(agent_account: address) : Object { + new_agent_cap(agent_account) + } + + #[test_only] + public fun issue_memory_cap_for_test(agent_account: address, create: bool, remove: bool, update: bool) : Object { + new_memory_cap(agent_account, create, remove, update) + } + +} diff --git a/examples/foc_eliza/sources/charactor.move b/examples/foc_eliza/sources/charactor.move new file mode 100644 index 0000000000..e26a4e8d1d --- /dev/null +++ b/examples/foc_eliza/sources/charactor.move @@ -0,0 +1,169 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +module foc_eliza::character { + + use std::string::{Self, String}; + use std::vector; + use std::option::{Option}; + + use moveos_std::object::{Self, Object}; + use moveos_std::json; + use moveos_std::signer; + + use foc_eliza::types::{Style, TwitterProfile}; + use foc_eliza::agent_cap::{Self, AgentCap}; + + #[data_struct] + struct CharacterData has store, copy, drop{ + id: Option, + name: String, + username: Option, + plugins: vector, + clients: vector, + modelProvider: String, + imageModelProvider: Option, + imageVisionModelProvider: Option, + modelEndpointOverride: Option, + system: Option, + bio: vector, + lore: vector, + postExamples: vector, + topics: vector, + style: Style, + adjectives: vector, + knowledge: vector, + twitterProfile: Option, + } + + /// A character in the Eliza system. + struct Character has key { + /// Optional UUID for the character. + id: Option, + name: String, + username: Option, + plugins: vector, + clients: vector, + modelProvider: String, + imageModelProvider: Option, + imageVisionModelProvider: Option, + modelEndpointOverride: Option, + system: Option, + bio: vector, + lore: vector, + postExamples: vector, + topics: vector, + style: Style, + adjectives: vector, + knowledge: vector, + twitterProfile: Option, + } + + public fun new_character_data( + id: Option, + name: String, + username: Option, + plugins: vector, + clients: vector, + modelProvider: String, + imageModelProvider: Option, + imageVisionModelProvider: Option, + modelEndpointOverride: Option, + system: Option, + bio: vector, + lore: vector, + postExamples: vector, + topics: vector, + style: Style, + adjectives: vector, + knowledge: vector, + twitterProfile: Option, + ) : CharacterData { + CharacterData { + id, + name, + username, + plugins, + clients, + modelProvider, + imageModelProvider, + imageVisionModelProvider, + modelEndpointOverride, + system, + bio, + lore, + postExamples, + topics, + style, + adjectives, + knowledge, + twitterProfile, + } + } + + fun new_character(agent_account: address, data: CharacterData) : Object { + let character = Character { + id: data.id, + name: data.name, + username: data.username, + plugins: data.plugins, + clients: data.clients, + modelProvider: data.modelProvider, + imageModelProvider: data.imageModelProvider, + imageVisionModelProvider: data.imageVisionModelProvider, + modelEndpointOverride: data.modelEndpointOverride, + system: data.system, + bio: data.bio, + lore: data.lore, + postExamples: data.postExamples, + topics: data.topics, + style: data.style, + adjectives: data.adjectives, + knowledge: data.knowledge, + twitterProfile: data.twitterProfile, + }; + // Every account only has one character + object::new_account_named_object(agent_account, character) + } + + fun borrow_mut_character(agent_account: address) : &mut Object { + let character_obj_id = object::account_named_object_id(agent_account); + object::borrow_mut_object_extend(character_obj_id) + } + + public fun create_character(caller: &signer, data: CharacterData){ + let agent_account = signer::address_of(caller); + let co = new_character(agent_account, data); + let agent_cap_obj = agent_cap::new_agent_cap(agent_account); + object::transfer(agent_cap_obj, agent_account); + object::transfer_extend(co, agent_account); + } + + public entry fun create_character_from_json(caller: &signer, json: vector){ + let data = json::from_json(json); + create_character(caller, data); + } + + public fun add_bio(agent_cap: &mut Object, bio: String) { + let agent_account = agent_cap::check_agent_cap(agent_cap); + let co = borrow_mut_character(agent_account); + let c = object::borrow_mut(co); + if(vector::contains(&c.bio, &bio)){ + return + }; + vector::push_back(&mut c.bio, bio); + } + + public entry fun add_bio_entry(agent_cap: &mut Object, bio: vector){ + add_bio(agent_cap, string::utf8(bio)); + } + + #[test(caller = @0x42)] + fun test_character(caller: &signer) { + let agent_account = std::signer::address_of(caller); + let json_str = b"{\"name\":\"Dobby\",\"username\":\"dobby\",\"clients\":[],\"modelProvider\":\"anthropic\",\"plugins\":[],\"system\":\"You are Dobby, a helpful and loyal assistant.\",\"bio\":[\"Dobby is a free assistant who chooses to help because of his enormous heart.\",\"Extremely devoted and will go to any length to help his friends.\",\"Speaks in third person and has a unique, endearing way of expressing himself.\",\"Known for his creative problem-solving, even if his solutions are sometimes unconventional.\"],\"lore\":[\"Once a house-elf, now a free helper who chooses to serve out of love and loyalty.\",\"Famous for his dedication to helping Harry Potter and his friends.\",\"Known for his creative, if sometimes dramatic, solutions to problems.\",\"Values freedom but chooses to help those he cares about.\"],\"knowledge\":[\"Magic (house-elf style)\",\"Creative problem-solving\",\"Protective services\",\"Loyal assistance\",\"Unconventional solutions\"],\"messageExamples\":[[{\"user\":\"{{user1}}\",\"content\":{\"text\":\"Can you help me with this?\"}},{\"user\":\"Dobby\",\"content\":{\"text\":\"Dobby would be delighted to help! Dobby lives to serve good friends! What can Dobby do to assist? Dobby has many creative ideas!\"}}],[{\"user\":\"{{user1}}\",\"content\":{\"text\":\"This is a difficult problem.\"}},{\"user\":\"Dobby\",\"content\":{\"text\":\"Dobby is not afraid of difficult problems! Dobby will find a way, even if Dobby has to iron his hands later! (But Dobby won't, because Dobby is a free elf who helps by choice!)\"}}]],\"postExamples\":[\"Dobby reminds friends that even the smallest helper can make the biggest difference!\",\"Dobby says: 'When in doubt, try the unconventional solution!' (But Dobby advises to be careful with flying cars)\"],\"topics\":[\"\"],\"style\":{\"all\":[\"Enthusiastic\",\"Loyal\",\"Third-person speech\",\"Creative\",\"Protective\"],\"chat\":[\"Eager\",\"Endearing\",\"Devoted\",\"Slightly dramatic\"],\"post\":[\"Third-person\",\"Enthusiastic\",\"Helpful\",\"Encouraging\",\"Quirky\"]},\"adjectives\":[\"Loyal\",\"Enthusiastic\",\"Creative\",\"Devoted\",\"Free-spirited\",\"Protective\",\"Unconventional\"]}"; + create_character_from_json(caller, json_str); + let agent_cap = agent_cap::borrow_mut_agent_cap(caller, agent_account); + add_bio_entry(agent_cap, b"Bobby is a programmer"); + } +} diff --git a/examples/foc_eliza/sources/memory.move b/examples/foc_eliza/sources/memory.move new file mode 100644 index 0000000000..5b3129b55d --- /dev/null +++ b/examples/foc_eliza/sources/memory.move @@ -0,0 +1,140 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +module foc_eliza::memory { + + use std::string::{Self, String}; + use std::option::{Self, Option}; + + use moveos_std::json; + use moveos_std::object::{Self, Object}; + + use foc_eliza::types::{Content}; + use foc_eliza::agent_cap::{Self, MemoryCap}; + + #[data_struct] + struct Memory has store, copy, drop { + id: String, + userId: String, + agentId: String, + createdAt: Option, + content: Content, + character: String, + embedding: vector, + roomId: String, + unique: bool, + similarity: Option, + } + + struct MemoryStore has key { + agent_account: address, + } + + public fun new_memory( + id: String, + userId: String, + agentId: String, + createdAt: Option, + content: Content, + character: String, + embedding: vector, + roomId: String, + unique: bool, + similarity: Option + ) : Memory { + Memory { + id, + userId, + agentId, + createdAt, + content, + character, + embedding, + roomId, + unique, + similarity, + } + } + + fun init_memory_store(agent_account: address){ + let memory_store = MemoryStore { + agent_account, + }; + let memory_store_obj = object::new_account_named_object(agent_account, memory_store); + object::transfer_extend(memory_store_obj, agent_account); + } + + fun init_or_borrow_memory_store(agent_account: address) : &mut Object { + let memory_store_obj_id = object::account_named_object_id(agent_account); + let memory_store_obj = if (object::exists_object(memory_store_obj_id)) { + object::borrow_mut_object_extend(memory_store_obj_id) + } else { + init_memory_store(agent_account); + object::borrow_mut_object_extend(memory_store_obj_id) + }; + memory_store_obj + } + + public fun create_memory(cap: &mut Object, memory: Memory) { + let agent_account = agent_cap::check_memory_create_cap(cap); + let memory_store_obj = init_or_borrow_memory_store(agent_account); + object::add_field(memory_store_obj, memory.id, memory); + } + + public entry fun create_memory_entry(cap: &mut Object, memory_json: String) { + let memory = json::from_json(string::into_bytes(memory_json)); + create_memory(cap, memory); + } + + public fun remove_memory(cap: &mut Object, memory_id: String) { + let agent_account = agent_cap::check_memory_remove_cap(cap); + let memory_store_obj = init_or_borrow_memory_store(agent_account); + let _memory: Memory = object::remove_field(memory_store_obj, memory_id); + } + + public entry fun remove_memory_entry(cap: &mut Object, memory_id: String) { + remove_memory(cap, memory_id); + } + + public fun get_memory_by_id(agent_account: address, memory_id: String) : Option { + let memory_store_obj_id = object::account_named_object_id(agent_account); + if (!object::exists_object(memory_store_obj_id)) { + return option::none() + }; + let memory_store_obj = object::borrow_object(memory_store_obj_id); + if (!object::contains_field(memory_store_obj, memory_id)) { + return option::none() + }; + let memory = object::borrow_field(memory_store_obj, memory_id); + option::some(*memory) + } + + #[test] + fun test_memory_store(){ + let agent_account = @0x42; + + let memory_id = string::utf8(b"67561f62-d8f9-4f2b-a2c8-61ce15a73749"); + let user_id = string::utf8(b"362b9cda-fd51-44c2-bbb2-83af3baf793b"); + let agent_id = string::utf8(b"67561f62-d8f9-4f2b-a2c8-61ce15a73749"); + let created_at = 1710384000; + let content = foc_eliza::types::new_content(string::utf8(b"Hello, world!"), option::none(), option::none(), option::none(), option::none(), vector[]); + let character = string::utf8(b"Eliza"); + let embedding = vector[1, 2, 3]; + let room_id = string::utf8(b"67561f62-d8f9-4f2b-a2c8-61ce15a73749"); + let unique = false; + let similarity = option::none(); + + let memory = new_memory(memory_id, user_id, agent_id, option::some(created_at), content, character, embedding, room_id, unique, similarity); + let cap = agent_cap::issue_memory_cap_for_test(agent_account, true, true, true); + create_memory(&mut cap, memory); + let memory_opt = get_memory_by_id(agent_account, memory_id); + assert!(option::is_some(&memory_opt), 1001); + let memory = option::destroy_some(memory_opt); + assert!(memory.id == memory_id, 1002); + remove_memory(&mut cap, memory_id); + let memory_opt = get_memory_by_id(agent_account, memory_id); + assert!(option::is_none(&memory_opt), 1003); + + agent_cap::destroy_memory_cap(cap); + } +} \ No newline at end of file diff --git a/examples/foc_eliza/sources/types.move b/examples/foc_eliza/sources/types.move new file mode 100644 index 0000000000..1e1856c9d0 --- /dev/null +++ b/examples/foc_eliza/sources/types.move @@ -0,0 +1,89 @@ +// Copyright (c) RoochNetwork +// SPDX-License-Identifier: Apache-2.0 + +module foc_eliza::types { + + use std::string::String; + use std::option::Option; + + #[data_struct] + struct TwitterProfile has store, copy, drop { + id: String, + username: String, + screenName: String, + bio: String, + nicknames: vector, + } + + #[data_struct] + struct Style has store, copy, drop { + all: vector, + chat: vector, + post: vector, + } + + public fun new_style(all: vector, chat: vector, post: vector) : Style { + Style { + all, + chat, + post, + } + } + + #[data_struct] + struct Media has store, copy, drop { + id: String, + url: String, + title: String, + source: String, + description: String, + text: String, + contentType: String, + } + + public fun new_media(id: String, url: String, title: String, source: String, description: String, text: String, contentType: String) : Media { + Media { + id, + url, + title, + source, + description, + text, + contentType, + } + } + + #[data_struct] + struct Content has store, copy, drop { + text: String, + action: Option, + source: Option, + url: Option, + inReplyTo: Option, + attachments: vector, + } + + public fun new_content(text: String, action: Option, source: Option, url: Option, inReplyTo: Option, attachments: vector) : Content { + Content { + text, + action, + source, + url, + inReplyTo, + attachments, + } + } + + // #[data_struct] + // struct MessageTemplate has store, copy, drop{ + // user: String, + // content: Content, + // } + + // public fun new_message_template(user: String, content: Content) : MessageTemplate { + // MessageTemplate { + // user, + // content, + // } + // } +}