mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-13 07:46:54 +08:00
feat: real SSE streaming for agent chat + data cleanup on startup
SSE Streaming: - Add POST /api/agent/chat/stream endpoint with real SSE streaming - Agent.HandleMessageStream() streams final LLM response via onEvent callback - Tool-calling rounds use non-streaming; tool status sent as 'tool' events - Frontend uses ReadableStream API instead of fake word-by-word setTimeout - SSE events: tool (tool execution), delta (text chunk), done (complete), error Data Maintenance: - Add cleanupOldData() on server startup (decisions >90d, equity >180d) - Add CleanOldRecords to DecisionStore and EquityStore - Add store-level logger for cleanup operations Exchange Traders: - Add GetAccountInfo() to Bitget, Bybit, Indodax, KuCoin, Lighter, OKX traders - Remove unused imports in Aster/Lighter traders
This commit is contained in:
@@ -2,7 +2,6 @@ package aster
|
||||
|
||||
import (
|
||||
"nofx/safe"
|
||||
"context"
|
||||
"crypto/ecdsa"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
@@ -25,7 +24,6 @@ import (
|
||||
|
||||
// AsterTrader Aster trading platform implementation
|
||||
type AsterTrader struct {
|
||||
ctx context.Context
|
||||
user string // Main wallet address (ERC20)
|
||||
signer string // API wallet address
|
||||
privateKey *ecdsa.PrivateKey // API wallet private key
|
||||
@@ -69,7 +67,6 @@ func NewAsterTrader(user, signer, privateKeyHex string) (*AsterTrader, error) {
|
||||
}
|
||||
|
||||
return &AsterTrader{
|
||||
ctx: context.Background(),
|
||||
user: user,
|
||||
signer: signer,
|
||||
privateKey: privKey,
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package aster
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
@@ -194,7 +193,6 @@ func NewAsterTraderTestSuite(t *testing.T) *AsterTraderTestSuite {
|
||||
|
||||
// Create mock trader using mock server's URL
|
||||
traderInstance := &AsterTrader{
|
||||
ctx: context.Background(),
|
||||
user: "0x1234567890123456789012345678901234567890",
|
||||
signer: "0xabcdefabcdefabcdefabcdefabcdefabcdefabcd",
|
||||
privateKey: privateKey,
|
||||
|
||||
@@ -193,6 +193,15 @@ func (t *BitgetTrader) doRequest(method, path string, body interface{}) ([]byte,
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("Bitget API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
respBody, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
@@ -77,6 +77,15 @@ func (t *BybitTrader) getTradesViaHTTP(startTime time.Time, limit int) ([]BybitT
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("Bybit API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
body, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
@@ -133,6 +133,15 @@ func (t *BybitTrader) getClosedPnLViaHTTP(startTime time.Time, limit int) ([]typ
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("Bybit API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
body, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
@@ -176,6 +176,13 @@ func (t *IndodaxTrader) doPrivateRequest(params url.Values) ([]byte, error) {
|
||||
if resp.StatusCode == http.StatusTooManyRequests {
|
||||
return nil, fmt.Errorf("rate limit exceeded, please try again later")
|
||||
}
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
snippet := string(data)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("Indodax API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
// Parse response to check success
|
||||
var apiResp IndodaxResponse
|
||||
|
||||
@@ -232,6 +232,15 @@ func (t *KuCoinTrader) doRequest(method, path string, body interface{}) ([]byte,
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("KuCoin API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
respBody, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
@@ -2,7 +2,6 @@ package lighter
|
||||
|
||||
import (
|
||||
"nofx/safe"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"math"
|
||||
@@ -59,7 +58,6 @@ type AccountResponse struct {
|
||||
|
||||
// LighterTraderV2 New implementation using official lighter-go SDK
|
||||
type LighterTraderV2 struct {
|
||||
ctx context.Context
|
||||
walletAddr string // Ethereum wallet address
|
||||
|
||||
client *http.Client
|
||||
@@ -127,7 +125,6 @@ func NewLighterTraderV2(walletAddr, apiKeyPrivateKeyHex string, apiKeyIndex int,
|
||||
httpClient := lighterHTTP.NewClient(baseURL)
|
||||
|
||||
trader := &LighterTraderV2{
|
||||
ctx: context.Background(),
|
||||
walletAddr: walletAddr,
|
||||
client: &http.Client{
|
||||
Timeout: 30 * time.Second,
|
||||
|
||||
@@ -546,6 +546,15 @@ func (t *LighterTraderV2) fetchMarketList() ([]MarketInfo, error) {
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("Lighter API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
body, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
@@ -228,6 +228,15 @@ func (t *OKXTrader) doRequest(method, path string, body interface{}) ([]byte, er
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode < 200 || resp.StatusCode >= 300 {
|
||||
errBody, _ := safe.ReadAllLimited(resp.Body)
|
||||
snippet := string(errBody)
|
||||
if len(snippet) > 256 {
|
||||
snippet = snippet[:256] + "..."
|
||||
}
|
||||
return nil, fmt.Errorf("OKX API HTTP error (status %d): %s", resp.StatusCode, snippet)
|
||||
}
|
||||
|
||||
respBody, err := safe.ReadAllLimited(resp.Body)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read response: %w", err)
|
||||
|
||||
Reference in New Issue
Block a user