Add projects
This commit is contained in:
parent
2d3a9ad623
commit
8b607dd700
1802 changed files with 503346 additions and 2 deletions
|
|
@ -0,0 +1,173 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type claudeStreamBuilder struct {
|
||||
model string
|
||||
messageID string
|
||||
toolUseID string
|
||||
index int
|
||||
inputTokens int
|
||||
}
|
||||
|
||||
func newClaudeStreamBuilder(model string) *claudeStreamBuilder {
|
||||
model = strings.TrimSpace(model)
|
||||
if model == "" {
|
||||
model = "claude-sonnet-4-6"
|
||||
}
|
||||
now := time.Now().UnixNano()
|
||||
return &claudeStreamBuilder{
|
||||
model: model,
|
||||
messageID: fmt.Sprintf("msg_%x", now),
|
||||
toolUseID: fmt.Sprintf("srvtoolu_%d", now),
|
||||
inputTokens: 85,
|
||||
}
|
||||
}
|
||||
|
||||
func (b *claudeStreamBuilder) buildStreamWithQuery(query string, hits []claudeWebSearchHit, answer string) []byte {
|
||||
var chunks []string
|
||||
chunks = append(chunks, b.event("message_start", map[string]any{
|
||||
"type": "message_start",
|
||||
"message": map[string]any{
|
||||
"id": b.messageID, "type": "message", "role": "assistant", "content": []any{},
|
||||
"model": b.model, "stop_reason": nil, "stop_sequence": nil,
|
||||
"usage": map[string]any{"input_tokens": b.inputTokens, "output_tokens": 0},
|
||||
},
|
||||
}))
|
||||
chunks = append(chunks, b.blockStart(b.index, map[string]any{
|
||||
"type": "server_tool_use", "id": b.toolUseID, "name": "web_search", "input": map[string]any{},
|
||||
}))
|
||||
partial, _ := json.Marshal(map[string]string{"query": query})
|
||||
chunks = append(chunks, b.event("content_block_delta", map[string]any{
|
||||
"type": "content_block_delta", "index": b.index,
|
||||
"delta": map[string]any{"type": "input_json_delta", "partial_json": string(partial)},
|
||||
}))
|
||||
chunks = append(chunks, b.event("content_block_stop", map[string]any{"type": "content_block_stop", "index": b.index}))
|
||||
b.index++
|
||||
|
||||
resultContent := webSearchResultBlocks(hits)
|
||||
chunks = append(chunks, b.blockStart(b.index, map[string]any{
|
||||
"type": "web_search_tool_result", "tool_use_id": b.toolUseID, "content": resultContent,
|
||||
}))
|
||||
chunks = append(chunks, b.event("content_block_stop", map[string]any{"type": "content_block_stop", "index": b.index}))
|
||||
b.index++
|
||||
|
||||
text := composeAnswerText(answer, hits)
|
||||
outputTokens := estimateTokens(text)
|
||||
chunks = append(chunks, b.blockStart(b.index, map[string]any{"type": "text", "text": ""}))
|
||||
chunks = append(chunks, b.event("content_block_delta", map[string]any{
|
||||
"type": "content_block_delta", "index": b.index,
|
||||
"delta": map[string]any{"type": "text_delta", "text": text},
|
||||
}))
|
||||
chunks = append(chunks, b.event("content_block_stop", map[string]any{"type": "content_block_stop", "index": b.index}))
|
||||
|
||||
chunks = append(chunks, b.event("message_delta", map[string]any{
|
||||
"type": "message_delta",
|
||||
"delta": map[string]any{"stop_reason": "end_turn", "stop_sequence": nil},
|
||||
"usage": map[string]any{
|
||||
"input_tokens": b.inputTokens, "output_tokens": outputTokens,
|
||||
"server_tool_use": map[string]any{"web_search_requests": 1},
|
||||
},
|
||||
}))
|
||||
chunks = append(chunks, b.event("message_stop", map[string]any{"type": "message_stop"}))
|
||||
return []byte(strings.Join(chunks, ""))
|
||||
}
|
||||
|
||||
func (b *claudeStreamBuilder) buildMessageJSON(query string, hits []claudeWebSearchHit, answer string) []byte {
|
||||
text := composeAnswerText(answer, hits)
|
||||
content := []map[string]any{
|
||||
{"type": "server_tool_use", "id": b.toolUseID, "name": "web_search", "input": map[string]string{"query": query}},
|
||||
{"type": "web_search_tool_result", "tool_use_id": b.toolUseID, "content": webSearchResultBlocks(hits)},
|
||||
{"type": "text", "text": text},
|
||||
}
|
||||
out := map[string]any{
|
||||
"id": b.messageID, "type": "message", "role": "assistant", "model": b.model,
|
||||
"content": content, "stop_reason": "end_turn", "stop_sequence": nil,
|
||||
"usage": map[string]any{
|
||||
"input_tokens": b.inputTokens, "output_tokens": estimateTokens(text),
|
||||
"server_tool_use": map[string]any{"web_search_requests": 1},
|
||||
},
|
||||
}
|
||||
raw, _ := json.Marshal(out)
|
||||
return raw
|
||||
}
|
||||
|
||||
func webSearchResultBlocks(hits []claudeWebSearchHit) []map[string]any {
|
||||
resultContent := make([]map[string]any, 0, len(hits))
|
||||
for _, hit := range hits {
|
||||
title := hit.Title
|
||||
if title == "" {
|
||||
title = hostFromURL(hit.URL)
|
||||
}
|
||||
resultContent = append(resultContent, map[string]any{
|
||||
"type": "web_search_result", "title": title, "url": hit.URL, "page_age": nil,
|
||||
})
|
||||
}
|
||||
return resultContent
|
||||
}
|
||||
|
||||
func (b *claudeStreamBuilder) event(eventType string, data map[string]any) string {
|
||||
raw, _ := json.Marshal(data)
|
||||
return fmt.Sprintf("event: %s\ndata: %s\n\n", eventType, string(raw))
|
||||
}
|
||||
|
||||
func (b *claudeStreamBuilder) blockStart(index int, block map[string]any) string {
|
||||
return b.event("content_block_start", map[string]any{
|
||||
"type": "content_block_start", "index": index, "content_block": block,
|
||||
})
|
||||
}
|
||||
|
||||
func composeAnswerText(answer string, hits []claudeWebSearchHit) string {
|
||||
if strings.TrimSpace(answer) != "" {
|
||||
return answer
|
||||
}
|
||||
if len(hits) == 0 {
|
||||
return "No web search results were returned."
|
||||
}
|
||||
var buf strings.Builder
|
||||
for i, hit := range hits {
|
||||
if i > 0 {
|
||||
buf.WriteString("\n\n")
|
||||
}
|
||||
if hit.Title != "" {
|
||||
buf.WriteString(hit.Title)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
if hit.URL != "" {
|
||||
buf.WriteString(hit.URL)
|
||||
buf.WriteString("\n")
|
||||
}
|
||||
if hit.Snippet != "" {
|
||||
buf.WriteString(hit.Snippet)
|
||||
}
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func hostFromURL(raw string) string {
|
||||
raw = strings.TrimSpace(raw)
|
||||
if raw == "" {
|
||||
return ""
|
||||
}
|
||||
withoutScheme := raw
|
||||
if idx := strings.Index(raw, "://"); idx >= 0 {
|
||||
withoutScheme = raw[idx+3:]
|
||||
}
|
||||
if slash := strings.Index(withoutScheme, "/"); slash >= 0 {
|
||||
return withoutScheme[:slash]
|
||||
}
|
||||
return withoutScheme
|
||||
}
|
||||
|
||||
func estimateTokens(text string) int {
|
||||
n := len([]rune(text)) / 4
|
||||
if n < 1 {
|
||||
return 1
|
||||
}
|
||||
return n
|
||||
}
|
||||
Loading…
Reference in a new issue