feat: add customer portal and RTC voice diagnostics

This commit is contained in:
Codex Local
2026-08-27 21:02:37 +08:00
parent 5ff1961d79
commit d97bbf7cf5
18 changed files with 4045 additions and 169 deletions
+251
View File
@@ -0,0 +1,251 @@
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
volcbase "github.com/volcengine/volc-sdk-golang/base"
)
type responseEnvelope struct {
ResponseMetadata struct {
Error *struct {
Code string `json:"Code"`
Message string `json:"Message"`
} `json:"Error"`
} `json:"ResponseMetadata"`
}
func main() {
akFile := flag.String("ak-file", "", "file containing the Volcengine AccessKey ID")
skFile := flag.String("sk-file", "", "file containing the Volcengine SecretAccessKey")
endpoint := flag.String("endpoint", "https://rtc.volcengineapi.com", "RTC OpenAPI endpoint")
rtcAppID := flag.String("rtc-app-id", "", "RTC application ID used for staged validation probes")
templateFile := flag.String("template-file", "", "voice-chat template bundle used for ProviderParams probes")
roomID := flag.String("room-id", "", "RTC room ID to inspect without starting a task")
flag.Parse()
if *akFile == "" || *skFile == "" {
panic("-ak-file and -sk-file are required")
}
ak := readSecret(*akFile)
sk := readSecret(*skFile)
credentials := volcbase.Credentials{AccessKeyID: ak, SecretAccessKey: sk, Service: "rtc", Region: "cn-north-1"}
client := &http.Client{Timeout: 15 * time.Second}
if *rtcAppID != "" && *roomID != "" {
queryRoom(client, credentials, *endpoint, *rtcAppID, *roomID)
return
}
for _, version := range []string{"2024-06-01", "2024-12-01", "2025-06-01"} {
probe(client, credentials, *endpoint, version, "empty", map[string]any{})
}
if *rtcAppID != "" {
version := "2024-12-01"
identity := map[string]any{
"AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task",
}
agent := map[string]any{
"TargetUserId": []string{"probe-user"}, "UserId": "probe-agent",
}
probe(client, credentials, *endpoint, version, "app-only", map[string]any{"AppId": *rtcAppID})
probe(client, credentials, *endpoint, version, "identity-only", map[string]any{
"AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task",
})
probe(client, credentials, *endpoint, version, "empty-config", map[string]any{
"AppId": *rtcAppID, "RoomId": "probe-room", "TaskId": "probe-task",
"Config": map[string]any{}, "AgentConfig": map[string]any{},
})
probe(client, credentials, *endpoint, version, "agent-valid", merge(identity, map[string]any{
"Config": map[string]any{}, "AgentConfig": agent,
}))
probe(client, credentials, *endpoint, version, "s2s-shell", merge(identity, map[string]any{
"Config": map[string]any{"S2SConfig": map[string]any{}}, "AgentConfig": agent,
}))
probe(client, credentials, *endpoint, version, "s2s-provider", merge(identity, map[string]any{
"Config": map[string]any{"S2SConfig": map[string]any{
"Provider": "volcano", "OutputMode": 0,
}}, "AgentConfig": agent,
}))
probe(client, credentials, *endpoint, version, "s2s-params-empty", merge(identity, map[string]any{
"Config": map[string]any{"S2SConfig": map[string]any{
"Provider": "volcano", "OutputMode": 0, "ProviderParams": map[string]any{},
}}, "AgentConfig": agent,
}))
if *templateFile != "" {
providerParams := readProviderParams(*templateFile)
for _, item := range []struct {
label string
keys []string
}{
{label: "params-app", keys: []string{"app"}},
{label: "params-dialog", keys: []string{"dialog"}},
{label: "params-asr", keys: []string{"asr"}},
{label: "params-tts", keys: []string{"tts"}},
{label: "params-full", keys: []string{"app", "dialog", "asr", "tts"}},
} {
selected := make(map[string]any, len(item.keys))
for _, key := range item.keys {
if value, ok := providerParams[key]; ok {
selected[key] = value
}
}
probeIdentity := merge(identity, map[string]any{"TaskId": "probe-" + item.label})
payload := merge(probeIdentity, map[string]any{
"Config": map[string]any{"S2SConfig": map[string]any{
"Provider": "volcano", "OutputMode": 0, "ProviderParams": selected,
}}, "AgentConfig": agent,
})
if probeAction(client, credentials, *endpoint, version, item.label, "StartVoiceChat", payload) {
probeAction(client, credentials, *endpoint, version, item.label+"-cleanup", "StopVoiceChat", probeIdentity)
}
}
fullTemplate := readHybridTemplate(*templateFile)
fullIdentity := merge(identity, map[string]any{"TaskId": "probe-full-template"})
fullTemplate = merge(fullTemplate, fullIdentity)
fullAgent, _ := fullTemplate["AgentConfig"].(map[string]any)
fullAgent["TargetUserId"] = []string{"probe-user"}
fullAgent["UserId"] = "probe-agent"
if probeAction(client, credentials, *endpoint, version, "full-template", "StartVoiceChat", fullTemplate) {
probeAction(client, credentials, *endpoint, version, "full-template-cleanup", "StopVoiceChat", fullIdentity)
}
}
probeAction(client, credentials, *endpoint, version, "cleanup", "StopVoiceChat", identity)
}
}
func queryRoom(client *http.Client, credentials volcbase.Credentials, endpoint, appID, roomID string) {
target, err := url.Parse(endpoint)
if err != nil {
panic(err)
}
query := target.Query()
query.Set("Action", "GetRoomOnlineUsers")
query.Set("Version", "2023-11-01")
query.Set("AppId", appID)
query.Set("RoomId", roomID)
query.Set("Property", "false")
target.RawQuery = query.Encode()
request, err := http.NewRequest(http.MethodGet, target.String(), nil)
if err != nil {
panic(err)
}
request.Host = target.Host
request.Header.Set("Accept", "application/json")
credentials.Sign(request)
response, err := client.Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1<<20))
if err != nil {
panic(err)
}
var decoded map[string]any
if err := json.Unmarshal(body, &decoded); err != nil {
panic(err)
}
encoded, _ := json.Marshal(decoded)
fmt.Printf("room-status=%d response=%s\n", response.StatusCode, encoded)
}
func readProviderParams(path string) map[string]any {
template := readHybridTemplate(path)
config, _ := template["Config"].(map[string]any)
s2s, _ := config["S2SConfig"].(map[string]any)
params, _ := s2s["ProviderParams"].(map[string]any)
if params == nil {
panic("template does not contain s2s_hybrid Config.S2SConfig.ProviderParams")
}
return params
}
func readHybridTemplate(path string) map[string]any {
raw, err := os.ReadFile(path)
if err != nil {
panic(err)
}
var bundle map[string]any
if err := json.Unmarshal(raw, &bundle); err != nil {
panic(err)
}
templates, _ := bundle["templates"].(map[string]any)
template, _ := templates["s2s_hybrid"].(map[string]any)
if template == nil {
panic("template does not contain s2s_hybrid")
}
return template
}
func merge(base map[string]any, extra map[string]any) map[string]any {
result := make(map[string]any, len(base)+len(extra))
for key, value := range base {
result[key] = value
}
for key, value := range extra {
result[key] = value
}
return result
}
func readSecret(path string) string {
value, err := os.ReadFile(path)
if err != nil {
panic(err)
}
return strings.TrimSpace(string(value))
}
func probe(client *http.Client, credentials volcbase.Credentials, endpoint, version, label string, payload map[string]any) {
probeAction(client, credentials, endpoint, version, label, "StartVoiceChat", payload)
}
func probeAction(client *http.Client, credentials volcbase.Credentials, endpoint, version, label, action string, payload map[string]any) bool {
target, err := url.Parse(endpoint)
if err != nil {
panic(err)
}
query := target.Query()
query.Set("Action", action)
query.Set("Version", version)
target.RawQuery = query.Encode()
bodyBytes, err := json.Marshal(payload)
if err != nil {
panic(err)
}
request, err := http.NewRequest(http.MethodPost, target.String(), bytes.NewReader(bodyBytes))
if err != nil {
panic(err)
}
request.Host = target.Host
request.Header.Set("Content-Type", "application/json")
request.Header.Set("Accept", "application/json")
credentials.Sign(request)
response, err := client.Do(request)
if err != nil {
fmt.Printf("label=%s version=%s transport_error=%v\n", label, version, err)
return false
}
defer response.Body.Close()
body, err := io.ReadAll(io.LimitReader(response.Body, 1<<20))
if err != nil {
fmt.Printf("label=%s version=%s status=%d read_error=%v\n", label, version, response.StatusCode, err)
return false
}
var envelope responseEnvelope
_ = json.Unmarshal(body, &envelope)
if envelope.ResponseMetadata.Error == nil {
fmt.Printf("label=%s version=%s status=%d code=none\n", label, version, response.StatusCode)
return response.StatusCode >= 200 && response.StatusCode < 300
}
fmt.Printf("label=%s version=%s status=%d code=%s message=%s\n", label, version, response.StatusCode,
envelope.ResponseMetadata.Error.Code, envelope.ResponseMetadata.Error.Message)
return false
}
+67
View File
@@ -0,0 +1,67 @@
package main
import (
"bytes"
"encoding/json"
"flag"
"fmt"
"io"
"net/http"
"net/url"
"os"
"strings"
"time"
volcbase "github.com/volcengine/volc-sdk-golang/base"
)
func main() {
akFile := flag.String("ak-file", "", "file containing the Volcengine AccessKey ID")
skFile := flag.String("sk-file", "", "file containing the Volcengine SecretAccessKey")
resourceID := flag.String("resource-id", "volc.speech.dialog", "speech service resource ID")
projectName := flag.String("project", "default", "Volcengine project name")
flag.Parse()
if *akFile == "" || *skFile == "" {
panic("-ak-file and -sk-file are required")
}
target, _ := url.Parse("https://open.volcengineapi.com")
query := target.Query()
query.Set("Action", "QuotaMonitoring")
query.Set("Version", "2025-05-21")
target.RawQuery = query.Encode()
today := time.Now().Format("2006-01-02")
body, _ := json.Marshal(map[string]any{
"ProjectName": *projectName,
"ResourceID": *resourceID,
"Start": today,
"End": today,
"Mode": "daily",
"QuotaType": "concurrency",
})
request, _ := http.NewRequest(http.MethodPost, target.String(), bytes.NewReader(body))
request.Host = target.Host
request.Header.Set("Content-Type", "application/json; charset=UTF-8")
credentials := volcbase.Credentials{
AccessKeyID: readSecret(*akFile), SecretAccessKey: readSecret(*skFile),
Service: "speech_saas_prod", Region: "cn-beijing",
}
credentials.Sign(request)
response, err := (&http.Client{Timeout: 15 * time.Second}).Do(request)
if err != nil {
panic(err)
}
defer response.Body.Close()
responseBody, _ := io.ReadAll(io.LimitReader(response.Body, 1<<20))
var decoded map[string]any
_ = json.Unmarshal(responseBody, &decoded)
encoded, _ := json.Marshal(decoded)
fmt.Printf("status=%d response=%s\n", response.StatusCode, encoded)
}
func readSecret(path string) string {
value, err := os.ReadFile(path)
if err != nil {
panic(err)
}
return strings.TrimSpace(string(value))
}