Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 6 additions & 1 deletion internal/aistudio/proxy_score_store.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@ import (
"path/filepath"
"strings"
"time"

"github.com/Mag1cFall/AIStudio2API/internal/paniclog"
)

// proxyScoreSaveDelay 是评分变化后延迟落盘的时间,避免每个请求都写文件
Expand Down Expand Up @@ -93,7 +95,10 @@ func (p *ProxyPool) scheduleSaveLocked() {
if p.scorePath == "" || p.closed || p.saveTimer != nil {
return
}
p.saveTimer = time.AfterFunc(proxyScoreSaveDelay, p.flushScores)
p.saveTimer = time.AfterFunc(proxyScoreSaveDelay, func() {
defer paniclog.Recover("proxy_score_store.go:flushScores")
p.flushScores()
})
}

// flushScores 执行一次延迟保存;保存失败时等下一次评分变化再重试
Expand Down
1 change: 1 addition & 0 deletions internal/aistudio/transport_http3_conn.go
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,7 @@ func (conn *http3Conn) flushDecoder() error {
func (conn *http3Conn) scheduleDecoderFlush() {
if conn.ackPending.CompareAndSwap(false, true) {
time.AfterFunc(time.Millisecond, func() {
defer paniclog.Recover("transport_http3_conn.go:scheduleDecoderFlush")
conn.ackPending.Store(false)
_ = conn.flushDecoder()
})
Expand Down
2 changes: 2 additions & 0 deletions internal/api/gemini_files.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import (
"time"

"github.com/Mag1cFall/AIStudio2API/internal/aistudio"
"github.com/Mag1cFall/AIStudio2API/internal/paniclog"
)

// geminiFilesURIPrefix 为 Files API 文件 URI 的前缀
Expand Down Expand Up @@ -151,6 +152,7 @@ func (uploads *geminiUploads) start(session *geminiUploadSession) (string, error
}
session.expires = time.Now().Add(geminiUploadTTL)
session.timer = time.AfterFunc(geminiUploadTTL, func() {
defer paniclog.Recover("gemini_files.go:uploadTTL")
session.mu.Lock()
defer session.mu.Unlock()
if !session.done && !time.Now().Before(session.expires) {
Expand Down
12 changes: 10 additions & 2 deletions internal/api/keepalive.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import (
"net/http"
"sync"
"time"

"github.com/Mag1cFall/AIStudio2API/internal/paniclog"
)

// nonstreamKeepaliveInterval 是非流式保活第一次写出和后续间隔。测试可以改短,避免真实等待 10 秒。
Expand Down Expand Up @@ -42,7 +44,10 @@ func (s *server) nonstreamKeepalive(w http.ResponseWriter, r *http.Request) (htt
interval = streamHeartbeatInterval
}
writer := &keepaliveWriter{ResponseWriter: w, ctx: r.Context(), interval: interval}
writer.timer = time.AfterFunc(interval, writer.beat)
writer.timer = time.AfterFunc(interval, func() {
defer paniclog.Recover("keepalive.go:nonstreamKeepalive.beat")
writer.beat()
})
return writer, writer.stop
}

Expand Down Expand Up @@ -126,7 +131,10 @@ type streamKeepalive struct {
func keepStreamAlive(w http.ResponseWriter, frame string) *streamKeepalive {
writer := &streamKeepalive{ResponseWriter: w, frame: func() string { return frame }}
writer.mu.Lock()
writer.timer = time.AfterFunc(streamHeartbeatInterval, writer.beat)
writer.timer = time.AfterFunc(streamHeartbeatInterval, func() {
defer paniclog.Recover("keepalive.go:streamKeepalive.beat")
writer.beat()
})
writer.mu.Unlock()
return writer
}
Expand Down
5 changes: 4 additions & 1 deletion internal/app/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -2638,7 +2638,10 @@ type roundDeadlineContext struct {
func newRoundDeadlineContext(parent context.Context, timeout time.Duration) (*roundDeadlineContext, context.CancelFunc) {
ctx := &roundDeadlineContext{parent: parent, timeout: timeout, done: make(chan struct{}), deadline: time.Now().Add(timeout)}
ctx.mu.Lock()
ctx.timer = time.AfterFunc(timeout, func() { ctx.finish(context.DeadlineExceeded) })
ctx.timer = time.AfterFunc(timeout, func() {
defer paniclog.Recover("runtime.go:roundDeadline")
ctx.finish(context.DeadlineExceeded)
})
ctx.stop = context.AfterFunc(parent, func() { ctx.finish(parent.Err()) })
ctx.mu.Unlock()
return ctx, func() { ctx.finish(context.Canceled) }
Expand Down
5 changes: 4 additions & 1 deletion internal/app/worker_manager.go
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,10 @@ func (manager *accountWorkerManager) markRuntimeBusy(account *accountWorker, cau
"WAA Worker 账户由其他进程占用 | 暂停调度=%s | 错误=%s", delay, strings.TrimSpace(cause.Error()),
))
}
time.AfterFunc(delay, manager.notifyScheduling)
time.AfterFunc(delay, func() {
defer paniclog.Recover("worker_manager.go:notifyScheduling")
manager.notifyScheduling()
})
return busyErr
}

Expand Down
10 changes: 8 additions & 2 deletions internal/camoufoxnative/worker.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,8 @@ func Start(ctx context.Context, options Options) (*Worker, error) {
if _, err := worker.chromeEvaluate(ctx, idleRenderingExpression); err != nil {
return nil, fmt.Errorf("停止 Camoufox 标签页绘制: %w", err)
}
worker.idle = time.AfterFunc(idleMinimizeDelay, worker.minimizeMemory)
worker.settle = time.AfterFunc(readyMinimizeDelay, worker.minimizeMemory)
worker.idle = time.AfterFunc(idleMinimizeDelay, worker.minimizeMemoryRecovered)
worker.settle = time.AfterFunc(readyMinimizeDelay, worker.minimizeMemoryRecovered)
}
failed = false
return worker, nil
Expand Down Expand Up @@ -263,6 +263,12 @@ const idleMinimizeDelay = 5 * time.Minute
// readyMinimizeDelay 为无头 Worker 就绪后发出一次 memory-pressure 的延迟,回收浏览器启动与首次载入留下的渲染目标与 GC 垃圾
const readyMinimizeDelay = 2 * time.Second

// minimizeMemoryRecovered 供 time.AfterFunc 调用,回调里的 panic 只记录不终止进程
func (worker *Worker) minimizeMemoryRecovered() {
defer paniclog.Recover("worker.go:minimizeMemory")
worker.minimizeMemory()
}

// minimizeMemory 在没有进行中的界面操作时发出 memory-pressure heap-minimize,各进程回收 GC 堆与软件 WebRender 的渲染缓存
func (worker *Worker) minimizeMemory() {
worker.frameMu.Lock()
Expand Down
1 change: 1 addition & 0 deletions internal/waa/loop.go
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ func (loop *eventLoop) schedule(delay time.Duration, job func()) {
}
var timer *time.Timer
timer = time.AfterFunc(delay, func() {
defer paniclog.Recover("loop.go:setTimeout")
loop.mu.Lock()
delete(loop.timers, timer)
loop.mu.Unlock()
Expand Down
6 changes: 5 additions & 1 deletion internal/waa/runtime.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import (
"sync"
"time"

"github.com/Mag1cFall/AIStudio2API/internal/paniclog"
"github.com/Mag1cFall/AIStudio2API/internal/tzcache"
"github.com/Mag1cFall/AIStudio2API/internal/waa/mozjs"
)
Expand Down Expand Up @@ -147,7 +148,10 @@ func newRuntime(ctx context.Context, options Options, prelude string) (*Runtime,
return nil, errors.New("WAA 事件循环已退出")
}
}
runtime.idle = time.AfterFunc(idleShrinkDelay, func() { loop.post(runtime.agent.cx.ShrinkGC) })
runtime.idle = time.AfterFunc(idleShrinkDelay, func() {
defer paniclog.Recover("runtime.go:idleShrink")
loop.post(runtime.agent.cx.ShrinkGC)
})
return runtime, nil
case <-loop.done:
loop.close()
Expand Down
2 changes: 1 addition & 1 deletion web/src/markdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export function gfmLinkify(md: Markdown) {
}

// markdown 渲染 Markdown 正文,原始 HTML 按文本转义,链接在新标签页打开
export const markdown = new MarkdownIt({ linkify: true, breaks: true })
export const markdown = new MarkdownIt({ linkify: true, breaks: true, html: false })
gfmLinkify(markdown)
markdown.renderer.rules.link_open = (tokens, index, options, _env, self) => {
tokens[index]!.attrSet('target', '_blank')
Expand Down