From ef461d4fc048b0e9f9199f6c6934b6d0ad48be4a Mon Sep 17 00:00:00 2001 From: 0xFirekeeper <0xFirekeeper@gmail.com> Date: Tue, 29 Apr 2025 20:08:32 +0700 Subject: [PATCH] Nebula t0-003 Integration Closes TOOL-4323 --- .../Thirdweb.AI/Thirdweb.AI.Tests.cs | 157 +++++++----------- Thirdweb/Thirdweb.AI/FeedbackClient.cs | 28 ---- Thirdweb/Thirdweb.AI/ThirdwebNebula.Types.cs | 156 ++++------------- Thirdweb/Thirdweb.AI/ThirdwebNebula.cs | 65 ++------ Thirdweb/Thirdweb.Utils/Constants.cs | 2 +- 5 files changed, 109 insertions(+), 299 deletions(-) delete mode 100644 Thirdweb/Thirdweb.AI/FeedbackClient.cs diff --git a/Thirdweb.Tests/Thirdweb.AI/Thirdweb.AI.Tests.cs b/Thirdweb.Tests/Thirdweb.AI/Thirdweb.AI.Tests.cs index 6a96bf0..44adcb6 100644 --- a/Thirdweb.Tests/Thirdweb.AI/Thirdweb.AI.Tests.cs +++ b/Thirdweb.Tests/Thirdweb.AI/Thirdweb.AI.Tests.cs @@ -1,13 +1,16 @@ // using System.Numerics; +using System.Numerics; using Thirdweb.AI; namespace Thirdweb.Tests.AI; public class NebulaTests : BaseTests { - // private const string NEBULA_TEST_CONTRACT = "0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8"; - // private const string NEBULA_TEST_USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; - // private const int NEBULA_TEST_CHAIN = 11155111; + private const string NEBULA_TEST_USDC_ADDRESS = "0x1c7D4B196Cb0C7B01d743Fbc6116a902379C7238"; + + private const string NEBULA_TEST_CONTRACT = "0xe2cb0eb5147b42095c2FfA6F7ec953bb0bE347D8"; + + private const int NEBULA_TEST_CHAIN = 11155111; public NebulaTests(ITestOutputHelper output) : base(output) { } @@ -33,104 +36,58 @@ public async Task Create_ResumesSession() Assert.Equal(sessionId, nebula.SessionId); } - // [Fact(Timeout = 120000)] - // public async Task Chat_Single_ReturnsResponse() - // { - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Chat( - // message: "What's the symbol of this contract?", - // context: new NebulaContext(contractAddresses: new List { NEBULA_TEST_CONTRACT }, chainIds: new List { NEBULA_TEST_CHAIN }) - // ); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.Contains("CAT", response.Message); - // } - - // [Fact(Timeout = 120000)] - // public async Task Chat_Single_NoContext_ReturnsResponse() - // { - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Chat(message: $"What's the symbol of this contract: {NEBULA_TEST_CONTRACT} (Sepolia)?"); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.Contains("CAT", response.Message); - // } - - // [Fact(Timeout = 120000)] - // public async Task Chat_Multiple_ReturnsResponse() - // { - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Chat( - // messages: new List - // { - // new("What's the symbol of this contract?", NebulaChatRole.User), - // new("The symbol is CAT", NebulaChatRole.Assistant), - // new("What's the name of this contract?", NebulaChatRole.User), - // }, - // context: new NebulaContext(contractAddresses: new List { NEBULA_TEST_CONTRACT }, chainIds: new List { NEBULA_TEST_CHAIN }) - // ); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.Contains("CatDrop", response.Message, StringComparison.OrdinalIgnoreCase); - // } + [Fact(Timeout = 120000)] + public async Task Chat_Single_ReturnsResponse() + { + var nebula = await ThirdwebNebula.Create(this.Client); + var response = await nebula.Chat(message: $"What's the symbol of this contract {NEBULA_TEST_CONTRACT}?", context: new NebulaContext(chainIds: new List() { NEBULA_TEST_CHAIN })); + Assert.NotNull(response); + Assert.NotNull(response.Message); + Assert.Contains("CAT", response.Message); + } - // [Fact(Timeout = 120000)] - // public async Task Chat_UnderstandsWalletContext() - // { - // var wallet = await PrivateKeyWallet.Generate(this.Client); - // var expectedAddress = await wallet.GetAddress(); - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Chat(message: "What is my wallet address?", wallet: wallet); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.Contains(expectedAddress, response.Message); - // } + [Fact(Timeout = 120000)] + public async Task Chat_Single_NoContext_ReturnsResponse() + { + var nebula = await ThirdwebNebula.Create(this.Client); + var response = await nebula.Chat(message: $"What's the symbol of this contract: {NEBULA_TEST_CONTRACT} (Sepolia)?"); + Assert.NotNull(response); + Assert.NotNull(response.Message); + Assert.Contains("CAT", response.Message); + } - // [Fact(Timeout = 120000)] - // public async Task Execute_ReturnsMessageAndReceipt() - // { - // var signer = await PrivateKeyWallet.Generate(this.Client); - // var wallet = await SmartWallet.Create(signer, NEBULA_TEST_CHAIN); - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Execute( - // new List - // { - // new("What's the address of vitalik.eth", NebulaChatRole.User), - // new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant), - // new("Approve 1 USDC to them", NebulaChatRole.User), - // }, - // wallet: wallet, - // context: new NebulaContext(contractAddresses: new List() { NEBULA_TEST_USDC_ADDRESS }) - // ); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.NotNull(response.TransactionReceipts); - // Assert.NotEmpty(response.TransactionReceipts); - // Assert.NotNull(response.TransactionReceipts[0].TransactionHash); - // Assert.True(response.TransactionReceipts[0].TransactionHash.Length == 66); - // } + [Fact(Timeout = 120000)] + public async Task Chat_UnderstandsWalletContext() + { + var wallet = await PrivateKeyWallet.Generate(this.Client); + var expectedAddress = await wallet.GetAddress(); + var nebula = await ThirdwebNebula.Create(this.Client); + var response = await nebula.Chat(message: "What is my wallet address?", wallet: wallet); + Assert.NotNull(response); + Assert.NotNull(response.Message); + Assert.Contains(expectedAddress, response.Message); + } - // [Fact(Timeout = 120000)] - // public async Task Execute_ReturnsMessageAndReceipts() - // { - // var signer = await PrivateKeyWallet.Generate(this.Client); - // var wallet = await SmartWallet.Create(signer, NEBULA_TEST_CHAIN); - // var nebula = await ThirdwebNebula.Create(this.Client); - // var response = await nebula.Execute( - // new List - // { - // new("What's the address of vitalik.eth", NebulaChatRole.User), - // new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant), - // new("Approve 1 USDC to them", NebulaChatRole.User), - // }, - // wallet: wallet, - // context: new NebulaContext(contractAddresses: new List() { NEBULA_TEST_USDC_ADDRESS }) - // ); - // Assert.NotNull(response); - // Assert.NotNull(response.Message); - // Assert.NotNull(response.TransactionReceipts); - // Assert.NotEmpty(response.TransactionReceipts); - // Assert.NotNull(response.TransactionReceipts[0].TransactionHash); - // Assert.True(response.TransactionReceipts[0].TransactionHash.Length == 66); - // } + [Fact(Timeout = 120000)] + public async Task Execute_ReturnsMessageAndReceipt() + { + var signer = await PrivateKeyWallet.Generate(this.Client); + var wallet = await SmartWallet.Create(signer, NEBULA_TEST_CHAIN); + var nebula = await ThirdwebNebula.Create(this.Client); + var response = await nebula.Execute( + new List + { + new("What's the address of vitalik.eth", NebulaChatRole.User), + new("The address of vitalik.eth is 0xd8dA6BF26964aF8E437eEa5e3616511D7G3a3298", NebulaChatRole.Assistant), + new($"Approve 1 USDC (this contract: {NEBULA_TEST_USDC_ADDRESS}) to them", NebulaChatRole.User), + }, + wallet: wallet + ); + Assert.NotNull(response); + Assert.NotNull(response.Message); + Assert.NotNull(response.TransactionReceipts); + Assert.NotEmpty(response.TransactionReceipts); + Assert.NotNull(response.TransactionReceipts[0].TransactionHash); + Assert.True(response.TransactionReceipts[0].TransactionHash.Length == 66); + } } diff --git a/Thirdweb/Thirdweb.AI/FeedbackClient.cs b/Thirdweb/Thirdweb.AI/FeedbackClient.cs deleted file mode 100644 index 2a9e4cc..0000000 --- a/Thirdweb/Thirdweb.AI/FeedbackClient.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System.Text; -using Newtonsoft.Json; - -namespace Thirdweb.AI; - -internal class FeedbackClient -{ - private readonly IThirdwebHttpClient _httpClient; - - public FeedbackClient(IThirdwebHttpClient httpClient) - { - this._httpClient = httpClient; - } - - /// - /// Submits feedback for a specific session and request. - /// - /// The feedback parameters to submit. - /// The submitted feedback details. - public async Task SubmitFeedbackAsync(FeedbackParams feedback) - { - var content = new StringContent(JsonConvert.SerializeObject(feedback), Encoding.UTF8, "application/json"); - var response = await this._httpClient.PostAsync($"{Constants.NEBULA_API_URL}/feedback", content); - _ = response.EnsureSuccessStatusCode(); - var responseContent = await response.Content.ReadAsStringAsync(); - return JsonConvert.DeserializeObject>(responseContent).Result; - } -} diff --git a/Thirdweb/Thirdweb.AI/ThirdwebNebula.Types.cs b/Thirdweb/Thirdweb.AI/ThirdwebNebula.Types.cs index 719a705..0c5b13d 100644 --- a/Thirdweb/Thirdweb.AI/ThirdwebNebula.Types.cs +++ b/Thirdweb/Thirdweb.AI/ThirdwebNebula.Types.cs @@ -1,3 +1,4 @@ +using System.Numerics; using Newtonsoft.Json; namespace Thirdweb.AI; @@ -59,14 +60,8 @@ internal class ChatParamsMultiMessages [JsonProperty("session_id")] internal string SessionId { get; set; } - [JsonProperty("config")] - internal ExecuteConfig Config { get; set; } - - [JsonProperty("execute_config")] - internal ExecuteConfig ExecuteConfig { get; set; } - - [JsonProperty("context_filter")] - internal ContextFilter ContextFilter { get; set; } + [JsonProperty("context")] + internal CompletionContext ContextFilter { get; set; } [JsonProperty("model_name")] internal string ModelName { get; set; } @@ -86,14 +81,8 @@ internal class ChatParamsSingleMessage [JsonProperty("session_id")] internal string SessionId { get; set; } - [JsonProperty("config")] - internal ExecuteConfig Config { get; set; } - - [JsonProperty("execute_config")] - internal ExecuteConfig ExecuteConfig { get; set; } - - [JsonProperty("context_filter")] - internal ContextFilter ContextFilter { get; set; } + [JsonProperty("context")] + internal CompletionContext ContextFilter { get; set; } [JsonProperty("model_name")] internal string ModelName { get; set; } @@ -123,16 +112,34 @@ public class ChatResponse /// /// Represents filters for narrowing down context in which operations are performed. /// -internal class ContextFilter +internal class CompletionContext { + [JsonProperty("session_id")] + public string SessionId { get; set; } = null; + + [JsonProperty("wallet_address")] + public string WalletAddress { get; set; } = null; + [JsonProperty("chain_ids")] - internal List ChainIds { get; set; } + public List ChainIds { get; set; } = null; +} - [JsonProperty("contract_addresses")] - internal List ContractAddresses { get; set; } +/// +/// Nebula representation of a smart contract. +/// +public class DeployedContract +{ + [JsonProperty("name")] + public string Name { get; set; } = string.Empty; + + [JsonProperty("address")] + public string Address { get; set; } + + [JsonProperty("chain_id")] + public BigInteger ChainId { get; set; } - [JsonProperty("wallet_addresses")] - internal List WalletAddresses { get; set; } + [JsonProperty("contract_type")] + public string ContractType { get; set; } = string.Empty; } /// @@ -149,95 +156,8 @@ internal class CreateSessionParams [JsonProperty("is_public")] internal bool? IsPublic { get; set; } - [JsonProperty("execute_config")] - internal ExecuteConfig ExecuteConfig { get; set; } - - [JsonProperty("context_filter")] - internal ContextFilter ContextFilter { get; set; } -} - -/// -/// Represents execution configuration options. -/// -internal class ExecuteConfig -{ - [JsonProperty("mode")] - internal string Mode { get; set; } = "client"; - - [JsonProperty("signer_wallet_address")] - internal string SignerWalletAddress { get; set; } - - [JsonProperty("engine_url")] - internal string EngineUrl { get; set; } - - [JsonProperty("engine_authorization_token")] - internal string EngineAuthorizationToken { get; set; } - - [JsonProperty("engine_backend_wallet_address")] - internal string EngineBackendWalletAddress { get; set; } - - [JsonProperty("smart_account_address")] - internal string SmartAccountAddress { get; set; } - - [JsonProperty("smart_account_factory_address")] - internal string SmartAccountFactoryAddress { get; set; } - - [JsonProperty("smart_account_session_key")] - internal string SmartAccountSessionKey { get; set; } -} - -/// -/// Represents a feedback submission. -/// -internal class Feedback -{ - [JsonProperty("id")] - internal string Id { get; set; } - - [JsonProperty("account_id")] - internal string AccountId { get; set; } - - [JsonProperty("session_id")] - internal string SessionId { get; set; } - - [JsonProperty("request_id")] - internal string RequestId { get; set; } - - [JsonProperty("feedback_rating")] - internal int? FeedbackRating { get; set; } - - [JsonProperty("feedback_response")] - internal string FeedbackResponse { get; set; } - - [JsonProperty("comment")] - internal string Comment { get; set; } - - [JsonProperty("created_at")] - internal DateTime? CreatedAt { get; set; } - - [JsonProperty("updated_at")] - internal DateTime? UpdatedAt { get; set; } -} - -/// -/// Parameters for submitting feedback. -/// -internal class FeedbackParams -{ - [JsonProperty("session_id")] - internal string SessionId { get; set; } - - [JsonProperty("request_id")] - internal string RequestId { get; set; } - - [JsonProperty("feedback_rating")] - internal int? FeedbackRating { get; set; } - - [JsonProperty("feedback_response")] - internal string FeedbackResponse { get; set; } - - [JsonProperty("comment")] - internal string Comment { get; set; } + [JsonProperty("context")] + internal CompletionContext ContextFilter { get; set; } } /// @@ -257,9 +177,6 @@ internal class Session [JsonProperty("is_public")] internal bool? IsPublic { get; set; } - [JsonProperty("execute_config")] - internal ExecuteConfig ExecuteConfig { get; set; } - [JsonProperty("title")] internal string Title { get; set; } @@ -272,8 +189,8 @@ internal class Session [JsonProperty("action")] internal List Action { get; set; } - [JsonProperty("context_filter")] - internal ContextFilter ContextFilter { get; set; } + [JsonProperty("context")] + internal CompletionContext ContextFilter { get; set; } [JsonProperty("archive_at")] internal DateTime? ArchiveAt { get; set; } @@ -302,11 +219,8 @@ internal class UpdateSessionParams [JsonProperty("is_public")] internal bool? IsPublic { get; set; } - [JsonProperty("execute_config")] - internal ExecuteConfig ExecuteConfig { get; set; } - - [JsonProperty("context_filter")] - internal ContextFilter ContextFilter { get; set; } + [JsonProperty("context")] + internal CompletionContext ContextFilter { get; set; } } /// diff --git a/Thirdweb/Thirdweb.AI/ThirdwebNebula.cs b/Thirdweb/Thirdweb.AI/ThirdwebNebula.cs index 2c9c99c..86b1c19 100644 --- a/Thirdweb/Thirdweb.AI/ThirdwebNebula.cs +++ b/Thirdweb/Thirdweb.AI/ThirdwebNebula.cs @@ -36,20 +36,17 @@ public class NebulaExecuteResult public class NebulaContext { public List ChainIds { get; set; } - public List ContractAddresses { get; set; } - public List WalletAddresses { get; set; } + public string WalletAddress { get; set; } /// /// Represents filters for narrowing down context in which operations are performed. /// /// The chain IDs to filter by. - /// The contract addresses to filter by. - /// The wallet addresses to filter by. - public NebulaContext(List chainIds = null, List contractAddresses = null, List walletAddresses = null) + /// The wallet addresses to filter by. + public NebulaContext(List chainIds = null, string walletAddress = null) { this.ChainIds = chainIds; - this.ContractAddresses = contractAddresses; - this.WalletAddresses = walletAddresses; + this.WalletAddress = walletAddress; } } @@ -60,7 +57,6 @@ public class ThirdwebNebula internal SessionManager Sessions { get; } internal ChatClient ChatClient { get; } internal ExecutionClient ExecuteClient { get; } - internal FeedbackClient FeedbackClient { get; } internal ThirdwebNebula(ThirdwebClient client) { @@ -68,7 +64,6 @@ internal ThirdwebNebula(ThirdwebClient client) this.Sessions = new SessionManager(httpClient); this.ChatClient = new ChatClient(httpClient); this.ExecuteClient = new ExecutionClient(httpClient); - this.FeedbackClient = new FeedbackClient(httpClient); } public static async Task Create(ThirdwebClient client, string sessionId = null, string model = Constants.NEBULA_DEFAULT_MODEL) @@ -102,7 +97,7 @@ public async Task Chat(string message, IThirdwebWallet wallet throw new ArgumentException("Message cannot be null or empty.", nameof(message)); } - var contextFiler = await PrepareContextFilter(wallet, context); + var contextFiler = await this.PrepareContextFilter(wallet, context); var result = await this.ChatClient.SendMessageAsync( new ChatParamsSingleMessage() @@ -110,7 +105,6 @@ public async Task Chat(string message, IThirdwebWallet wallet SessionId = this.SessionId, Message = message, ContextFilter = contextFiler, - ExecuteConfig = wallet == null ? null : new ExecuteConfig() { Mode = "client", SignerWalletAddress = await wallet.GetAddress() } } ); @@ -126,7 +120,7 @@ public async Task Chat(List messages, IThir throw new ArgumentException("Messages cannot be null or empty.", nameof(messages)); } - var contextFiler = await PrepareContextFilter(wallet, context); + var contextFiler = await this.PrepareContextFilter(wallet, context); var result = await this.ChatClient.SendMessagesAsync( new ChatParamsMultiMessages() @@ -134,7 +128,6 @@ public async Task Chat(List messages, IThir SessionId = this.SessionId, Messages = messages.Select(prompt => new ChatMessage() { Content = prompt.Message, Role = prompt.Role.ToString().ToLower() }).ToList(), ContextFilter = contextFiler, - ExecuteConfig = wallet == null ? null : new ExecuteConfig() { Mode = "client", SignerWalletAddress = await wallet.GetAddress() } } ); @@ -155,14 +148,13 @@ public async Task Execute(string message, IThirdwebWallet w throw new ArgumentException("Wallet cannot be null.", nameof(wallet)); } - var contextFiler = await PrepareContextFilter(wallet, context); + var contextFiler = await this.PrepareContextFilter(wallet, context); var result = await this.ExecuteClient.ExecuteAsync( new ChatParamsSingleMessage() { SessionId = this.SessionId, Message = message, ContextFilter = contextFiler, - ExecuteConfig = new ExecuteConfig() { Mode = "client", SignerWalletAddress = await wallet.GetAddress() } } ); @@ -190,14 +182,13 @@ public async Task Execute(List messages, throw new ArgumentException("Wallet cannot be null.", nameof(wallet)); } - var contextFiler = await PrepareContextFilter(wallet, context); + var contextFiler = await this.PrepareContextFilter(wallet, context); var result = await this.ExecuteClient.ExecuteBatchAsync( new ChatParamsMultiMessages() { SessionId = this.SessionId, Messages = messages.Select(prompt => new ChatMessage() { Content = prompt.Message, Role = prompt.Role.ToString().ToLower() }).ToList(), ContextFilter = contextFiler, - ExecuteConfig = new ExecuteConfig() { Mode = "client", SignerWalletAddress = await wallet.GetAddress() } } ); @@ -213,52 +204,28 @@ public async Task Execute(List messages, } } - private static async Task PrepareContextFilter(IThirdwebWallet wallet, NebulaContext context) + private async Task PrepareContextFilter(IThirdwebWallet wallet, NebulaContext context) { context ??= new NebulaContext(); if (wallet != null) { - var walletAddress = await wallet.GetAddress(); - - // Add the wallet address to the context - if (context.WalletAddresses == null || context.WalletAddresses.Count == 0) - { - context.WalletAddresses = new List() { walletAddress }; - } - else if (!context.WalletAddresses.Contains(walletAddress)) - { - context.WalletAddresses.Add(walletAddress); - } - - // If it's a smart wallet, add the contract address and chain ID to the context + context.WalletAddress ??= await wallet.GetAddress(); if (wallet is SmartWallet smartWallet) { - // if (context.ContractAddresses == null || context.ContractAddresses.Count == 0) - // { - // context.ContractAddresses = new List() { walletAddress }; - // } - // else if (!context.ContractAddresses.Contains(walletAddress)) - // { - // context.ContractAddresses.Add(walletAddress); - // } - - if (context.ChainIds == null || context.ChainIds.Count == 0) - { - context.ChainIds = new List() { smartWallet.ActiveChainId }; - } - else if (!context.ChainIds.Contains(smartWallet.ActiveChainId)) + context.ChainIds ??= new List(); + if (context.ChainIds.Count == 0 || !context.ChainIds.Contains(smartWallet.ActiveChainId)) { context.ChainIds.Add(smartWallet.ActiveChainId); } } } - return new ContextFilter() + return new CompletionContext() { - ChainIds = context?.ChainIds?.Select(id => id.ToString()).ToList(), - ContractAddresses = context?.ContractAddresses, - WalletAddresses = context?.WalletAddresses + SessionId = this.SessionId, + ChainIds = context?.ChainIds?.Select(id => id).ToList(), + WalletAddress = context?.WalletAddress }; } diff --git a/Thirdweb/Thirdweb.Utils/Constants.cs b/Thirdweb/Thirdweb.Utils/Constants.cs index 5b51be5..ba13ddb 100644 --- a/Thirdweb/Thirdweb.Utils/Constants.cs +++ b/Thirdweb/Thirdweb.Utils/Constants.cs @@ -40,7 +40,7 @@ public static class Constants "0x0101010101010101010101010101010101010101000000000000000000000000000000000000000000000000000001010101010100000000000000000000000000000000000000000000000000000000000000000101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101010101"; internal const string ENS_REGISTRY_ADDRESS = "0xce01f8eee7E479C928F8919abD53E553a36CeF67"; - internal const string NEBULA_DEFAULT_MODEL = "t0-001"; + internal const string NEBULA_DEFAULT_MODEL = "t0-003"; internal const string ENTRYPOINT_V06_ABI = /*lang=json,strict*/