feat: add token search in modal when confirming address

This commit is contained in:
shokollm
2026-04-10 12:14:32 +00:00
parent 297a185215
commit 6f23b322d3
5 changed files with 125 additions and 3 deletions

View File

@@ -225,6 +225,7 @@ def chat(
success=result.get("success", False), success=result.get("success", False),
strategy_needs_confirmation=result.get("strategy_needs_confirmation", False), strategy_needs_confirmation=result.get("strategy_needs_confirmation", False),
strategy_data=result.get("strategy_data") if result.get("strategy_needs_confirmation") else None, strategy_data=result.get("strategy_data") if result.get("strategy_needs_confirmation") else None,
token_search_results=result.get("token_search_results"),
) )

View File

@@ -151,6 +151,7 @@ class BotChatResponse(BaseModel):
success: bool = False success: bool = False
strategy_needs_confirmation: Optional[bool] = False strategy_needs_confirmation: Optional[bool] = False
strategy_data: Optional[dict] = None strategy_data: Optional[dict] = None
token_search_results: Optional[List[dict]] = None
class SignalResponse(BaseModel): class SignalResponse(BaseModel):

View File

@@ -165,12 +165,43 @@ class ConversationalAgent:
# Check if token_address is missing in strategy_update # Check if token_address is missing in strategy_update
strategy_needs_confirmation = False strategy_needs_confirmation = False
token_search_results = None
if strategy_update: if strategy_update:
# Extract token name from conditions
token_name = None
for cond in strategy_update.get("conditions", []): for cond in strategy_update.get("conditions", []):
if not cond.get("token_address"): if not cond.get("token_address") and cond.get("token"):
token_name = cond.get("token")
strategy_needs_confirmation = True strategy_needs_confirmation = True
break break
# Search for token if name is found
if strategy_needs_confirmation and token_name:
try:
from ..ave.client import AveCloudClient
from ...core.config import get_settings
settings = get_settings()
ave_client = AveCloudClient(
api_key=settings.AVE_API_KEY,
plan=settings.AVE_API_PLAN
)
# Run async search in sync context
import asyncio
tokens = asyncio.run(ave_client.get_tokens(query=token_name, chain="bsc", limit=5))
if tokens:
token_search_results = [
{
"symbol": t.get("symbol", ""),
"name": t.get("name", ""),
"address": t.get("id") or t.get("contract_address", ""),
"chain": t.get("chain", "bsc")
}
for t in tokens
]
except Exception as e:
print(f"Token search error: {e}")
# Only update strategy if token_address is provided # Only update strategy if token_address is provided
if strategy_update and strategy_needs_confirmation: if strategy_update and strategy_needs_confirmation:
# Don't auto-save - user needs to confirm token address # Don't auto-save - user needs to confirm token address
@@ -181,6 +212,7 @@ class ConversationalAgent:
"strategy_updated": False, "strategy_updated": False,
"strategy_needs_confirmation": True, "strategy_needs_confirmation": True,
"strategy_data": strategy_update, "strategy_data": strategy_update,
"token_search_results": token_search_results,
"success": True "success": True
} }

View File

@@ -132,4 +132,12 @@ export interface BotChatResponse {
success: boolean; success: boolean;
strategy_needs_confirmation?: boolean; strategy_needs_confirmation?: boolean;
strategy_data?: StrategyConfig | null; strategy_data?: StrategyConfig | null;
token_search_results?: TokenSearchResult[] | null;
}
export interface TokenSearchResult {
symbol: string;
name: string;
address: string;
chain: string;
} }

View File

@@ -5,6 +5,7 @@
import { isAuthenticated, isLoading, chatStore, addMessage, setMessages, clearChat, currentBotStore, setCurrentBot } from '$lib/stores'; import { isAuthenticated, isLoading, chatStore, addMessage, setMessages, clearChat, currentBotStore, setCurrentBot } from '$lib/stores';
import { api } from '$lib/api'; import { api } from '$lib/api';
import { ChatInterface, StrategyPreview } from '$lib/components'; import { ChatInterface, StrategyPreview } from '$lib/components';
import type { TokenSearchResult } from '$lib/api';
let botId = $derived($page.params.id); let botId = $derived($page.params.id);
let isSending = $state(false); let isSending = $state(false);
@@ -15,6 +16,7 @@
let pendingStrategyData = $state<any>(null); let pendingStrategyData = $state<any>(null);
let tokenAddressInput = $state(''); let tokenAddressInput = $state('');
let confirmingMessage = $state(''); let confirmingMessage = $state('');
let tokenSearchResults = $state<TokenSearchResult[]>([]);
onMount(async () => { onMount(async () => {
if (!$isAuthenticated && !$isLoading) { if (!$isAuthenticated && !$isLoading) {
@@ -67,6 +69,7 @@
pendingStrategyData = response.strategy_data; pendingStrategyData = response.strategy_data;
confirmingMessage = response.response; confirmingMessage = response.response;
tokenAddressInput = ''; tokenAddressInput = '';
tokenSearchResults = response.token_search_results || [];
showTokenConfirm = true; showTokenConfirm = true;
} }
@@ -134,12 +137,18 @@
showTokenConfirm = false; showTokenConfirm = false;
pendingStrategyData = null; pendingStrategyData = null;
tokenAddressInput = ''; tokenAddressInput = '';
tokenSearchResults = [];
}
function selectTokenResult(result: TokenSearchResult) {
tokenAddressInput = result.address;
} }
function cancelTokenConfirm() { function cancelTokenConfirm() {
showTokenConfirm = false; showTokenConfirm = false;
pendingStrategyData = null; pendingStrategyData = null;
tokenAddressInput = ''; tokenAddressInput = '';
tokenSearchResults = [];
} }
</script> </script>
@@ -151,9 +160,23 @@
{#if showTokenConfirm} {#if showTokenConfirm}
<div class="modal-overlay" onclick={cancelTokenConfirm}> <div class="modal-overlay" onclick={cancelTokenConfirm}>
<div class="modal-content" onclick={(e) => e.stopPropagation()}> <div class="modal-content" onclick={(e) => e.stopPropagation()}>
<h3>Confirm Token Address</h3> <h3>Select Token Address</h3>
<p class="modal-message">{confirmingMessage}</p> <p class="modal-message">{confirmingMessage}</p>
<p class="modal-hint">Please enter the BSC contract address for the token:</p>
{#if tokenSearchResults.length > 0}
<div class="token-results">
<p class="modal-hint">Select a token:</p>
{#each tokenSearchResults as result}
<button class="token-result" onclick={() => selectTokenResult(result)}>
<span class="token-symbol">{result.symbol}</span>
<span class="token-name">{result.name}</span>
<span class="token-address">{result.address.slice(0, 10)}...{result.address.slice(-8)}</span>
</button>
{/each}
</div>
<p class="modal-divider">or enter manually:</p>
{/if}
<input type="text" class="token-input" bind:value={tokenAddressInput} placeholder="0x..."/> <input type="text" class="token-input" bind:value={tokenAddressInput} placeholder="0x..."/>
<div class="modal-actions"> <div class="modal-actions">
<button class="btn btn-secondary" onclick={cancelTokenConfirm}>Cancel</button> <button class="btn btn-secondary" onclick={cancelTokenConfirm}>Cancel</button>
@@ -363,4 +386,61 @@
opacity: 0.5; opacity: 0.5;
cursor: not-allowed; cursor: not-allowed;
} }
/* Token Results */
.token-results {
max-height: 200px;
overflow-y: auto;
margin-bottom: 1rem;
}
.token-result {
width: 100%;
display: flex;
align-items: center;
gap: 0.75rem;
padding: 0.75rem;
background: rgba(255, 255, 255, 0.05);
border: 1px solid rgba(255, 255, 255, 0.1);
border-radius: 8px;
margin-bottom: 0.5rem;
cursor: pointer;
text-align: left;
color: #fff;
transition: background 0.2s;
}
.token-result:hover {
background: rgba(102, 126, 234, 0.2);
border-color: rgba(102, 126, 234, 0.5);
}
.token-result:last-child {
margin-bottom: 0;
}
.token-symbol {
font-weight: 600;
color: #667eea;
min-width: 60px;
}
.token-name {
flex: 1;
color: #ccc;
font-size: 0.9rem;
}
.token-address {
font-size: 0.75rem;
color: #666;
font-family: 'Monaco', 'Menlo', monospace;
}
.modal-divider {
text-align: center;
color: #666;
font-size: 0.85rem;
margin: 1rem 0;
}
</style> </style>