源码索引 / CURRENT IMPLEMENTATION
cmd/trade-demo/main.go
162 行 · 点击行号可定位;函数链接随声明位置更新,不手填行号。
跳到函数 / 声明
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"os"
"sort"
"strings"
"time"
spine "pysolate-spine"
)
type config struct {
wasm, source, mode, memory, symbol, inputs string
price, cash, limit, quantity int
allowOrder, showTransformed bool
readDelay, timeLimit time.Duration
}
func main() {
var c config
flag.StringVar(&c.wasm, "wasm", "dist/spine.wasm", "existing Guest artifact")
flag.StringVar(&c.source, "source", "examples/trading.py", "Python trading example")
flag.StringVar(&c.mode, "mode", "plm", "normal, plm, or prefix")
flag.StringVar(&c.memory, "memory", "fresh", "fresh, copy, or cow (Linux)")
flag.StringVar(&c.symbol, "symbol", "ACME", "fictional symbol used by the local market")
flag.IntVar(&c.price, "price", 100, "frozen simulated price, in whole demo units")
flag.IntVar(&c.cash, "cash", 2000, "initial simulated cash")
flag.IntVar(&c.limit, "limit", 110, "Python buy-price threshold")
flag.IntVar(&c.quantity, "quantity", 10, "Python requested quantity")
flag.StringVar(&c.inputs, "inputs", "", "JSON overrides for Python inputs; never grants Host approval")
flag.BoolVar(&c.allowOrder, "allow-order", false, "allow in-memory simulated orders; no broker is contacted")
flag.BoolVar(&c.showTransformed, "show-transformed", false, "print actual Guest-transformed source")
flag.DurationVar(&c.readDelay, "read-delay", 0, "optional simulated latency of each snapshot read, e.g. 300ms")
flag.DurationVar(&c.timeLimit, "timeout", 30*time.Second, "whole demo timeout")
flag.Parse()
if err := run(c, os.Stdout, os.Stderr); err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}
func run(c config, stdout, stderr io.Writer) error {
if c.mode != "normal" && c.mode != "plm" && c.mode != "prefix" {
return fmt.Errorf("unknown mode %q", c.mode)
}
if c.memory != "fresh" && c.memory != "copy" && c.memory != "cow" {
return fmt.Errorf("unknown memory mode %q", c.memory)
}
if c.price <= 0 || c.cash < 0 || c.quantity <= 0 || c.limit < 0 || c.readDelay < 0 || c.timeLimit <= 0 {
return fmt.Errorf("invalid simulated market settings")
}
inputs := map[string]any{"symbol": c.symbol, "limit": c.limit, "quantity": c.quantity}
if c.inputs != "" {
if err := json.Unmarshal([]byte(c.inputs), &inputs); err != nil {
return err
}
if inputs == nil {
return fmt.Errorf("inputs must be a JSON object")
}
}
wasm, err := os.ReadFile(c.wasm)
if err != nil {
return err
}
source, err := os.ReadFile(c.source)
if err != nil {
return err
}
t := newTrace(stderr)
m := newMarket(c.symbol, c.price, c.cash, c.allowOrder, c.readDelay, t)
t.log("demo.config", "", map[string]any{"simulation": true, "mode": c.mode, "memory": c.memory, "price": c.price, "cash": c.cash, "allow_order": c.allowOrder, "read_delay": c.readDelay.String()})
ctx, cancel := context.WithTimeout(context.Background(), c.timeLimit)
defer cancel()
create := spine.New
if c.memory == "copy" {
create = spine.NewPrepared
} else if c.memory == "cow" {
create = spine.NewPreparedCOW
}
manifest := m.tools()
earlyReads := make([]string, 0, len(manifest))
for name, spec := range manifest {
if spec.AllowEarlyRead {
earlyReads = append(earlyReads, name)
}
}
sort.Strings(earlyReads)
t.log("runner.create", "", nil)
runner, err := create(ctx, wasm, manifest)
if err != nil {
return err
}
defer runner.Close(context.Background())
t.log("runner.ready", "", map[string]any{"early_reads": earlyReads, "buy_early": manifest["buy"].AllowEarlyRead})
var output spine.Output
if c.mode == "prefix" {
chunks := make(chan string)
producerDone := make(chan struct{})
producerCtx, stopProducer := context.WithCancel(ctx)
go func() {
defer close(producerDone)
defer t.closeSource(chunks)
for i, chunk := range strings.SplitAfter(string(source), "\n") {
if chunk == "" {
continue
}
t.log("source.send", "", map[string]any{"chunk": i + 1, "text": chunk})
select {
case chunks <- chunk:
case <-producerCtx.Done():
t.log("source.cancelled", "", nil)
return
}
}
}()
output, err = runner.RunPrefix(ctx, chunks, inputs)
stopProducer()
<-producerDone
} else {
t.log("source.complete", "", map[string]any{"bytes": len(source)})
execute := runner.Run
if c.mode == "plm" {
execute = runner.RunPLM
}
output, err = execute(ctx, string(source), inputs)
}
t.log("run.finished", "", map[string]any{"transformed": output.Transformed != "", "error": errorText(err)})
if c.showTransformed {
fmt.Fprintln(stderr, "--- actual transformed source ---")
if output.Transformed == "" {
fmt.Fprintln(stderr, "(no AST rewrite)")
} else {
fmt.Fprintln(stderr, output.Transformed)
}
}
if output.Stdout != "" {
t.log("guest.stdout", "", output.Stdout)
}
if err != nil {
return err
}
report := struct {
Simulation bool `json:"simulation"`
Mode string `json:"mode"`
Result json.RawMessage `json:"result"`
Calls map[string]int `json:"tool_calls"`
Attempts int `json:"buy_attempts"`
Orders []order `json:"orders"`
Remaining int `json:"remaining_cash"`
}{true, c.mode, output.Value, m.calls, m.attempts, append([]order{}, m.orders...), m.remainingCash}
return json.NewEncoder(stdout).Encode(report)
}
func errorText(err error) string {
if err == nil {
return ""
}
return err.Error()
}