// kbdb_ingest — 把 input 寫入 KBDB(POST /blocks/ingest) // thin wrapper:透過 host function http_request 呼叫 KBDB API // //go:build tinygo package main import ( "encoding/json" "io" "os" "unsafe" ) //go:wasmimport u6u http_request func hostHttpRequest( urlPtr uintptr, urlLen uint32, methodPtr uintptr, methodLen uint32, headersPtr uintptr, headersLen uint32, bodyPtr uintptr, bodyLen uint32, outPtr uintptr, outLenPtr uintptr, ) uint32 type Input struct { KBDBUrl string `json:"kbdb_url"` // optional, default https://kbdb.finally.click APIKey string `json:"api_key"` // 必填(pk_live_xxx 或 ak_xxx) Text string `json:"text"` // 必填(block 內容) UserID string `json:"user_id"` // 必填(namespace prefix 對應) Source string `json:"source"` // optional PageName string `json:"page_name"` // optional // 註:本零件硬編碼 skip_llm=true(mira 場景定型貼文,不需 KBDB triplet 抽取)。 // 若需 LLM 抽取,未來另建 kbdb_ingest_with_llm 零件。 } var dummy [1]byte func safePtr(b []byte) (uintptr, uint32) { if len(b) == 0 { return uintptr(unsafe.Pointer(&dummy[0])), 0 } return uintptr(unsafe.Pointer(&b[0])), uint32(len(b)) } func main() { raw, err := io.ReadAll(os.Stdin) if err != nil { writeError("failed to read stdin: " + err.Error()) return } var input Input if err := json.Unmarshal(raw, &input); err != nil { writeError("invalid input JSON: " + err.Error()) return } if input.APIKey == "" { writeError("api_key 必填") return } if input.Text == "" { writeError("text 必填") return } if input.UserID == "" { writeError("user_id 必填") return } kbdbURL := input.KBDBUrl if kbdbURL == "" { kbdbURL = "https://kbdb.finally.click" } // 構造 KBDB ingest 的 body(只含 KBDB 認得的欄位) type ingestBody struct { Text string `json:"text"` UserID string `json:"user_id"` Source string `json:"source,omitempty"` PageName string `json:"page_name,omitempty"` SkipLLM *bool `json:"skip_llm,omitempty"` } skipLLM := true body := ingestBody{ Text: input.Text, UserID: input.UserID, Source: input.Source, PageName: input.PageName, SkipLLM: &skipLLM, } bodyBytes, _ := json.Marshal(body) headers := map[string]string{ "Content-Type": "application/json", "Authorization": "Bearer " + input.APIKey, } headersBytes, _ := json.Marshal(headers) url := kbdbURL + "/blocks/ingest" method := "POST" urlBytes := []byte(url) methodBytes := []byte(method) outBuf := make([]byte, 65536) var outLen uint32 urlPtr, urlLen := safePtr(urlBytes) methodPtr, methodLen := safePtr(methodBytes) headersPtr, headersLen := safePtr(headersBytes) bodyPtr, bodyLenU := safePtr(bodyBytes) result := hostHttpRequest( urlPtr, urlLen, methodPtr, methodLen, headersPtr, headersLen, bodyPtr, bodyLenU, uintptr(unsafe.Pointer(&outBuf[0])), uintptr(unsafe.Pointer(&outLen)), ) if result != 0 { writeError("KBDB ingest request failed (host_http_request returned non-zero)") return } // KBDB 回傳格式:{"blocks_injected": N, "triplets_injected": M, ...} respStr := string(outBuf[:outLen]) // 嘗試 parse 確認是 JSON(若 KBDB 回 error 也透傳) var kbdbResp map[string]interface{} if err := json.Unmarshal([]byte(respStr), &kbdbResp); err != nil { writeError("KBDB returned non-JSON: " + respStr) return } // 若 KBDB 回 error 欄位(401/400 etc.),透傳 if _, hasErr := kbdbResp["error"]; hasErr { out, _ := json.Marshal(map[string]interface{}{ "success": false, "error": kbdbResp["error"], }) os.Stdout.Write(out) return } out, _ := json.Marshal(map[string]interface{}{ "success": true, "data": kbdbResp, }) os.Stdout.Write(out) } func writeError(msg string) { out, _ := json.Marshal(map[string]interface{}{"success": false, "error": msg}) os.Stdout.Write(out) }