From 3bf0ac628d4044a7cd5dcb54c61816c61d744238 Mon Sep 17 00:00:00 2001 From: Rogee Date: Sat, 3 Oct 2026 20:20:26 +0800 Subject: [PATCH] Verify bounded real AI and OSS access without dialing --- .../evidence/real-ai-oss-one-shot-20261003.md | 8 ++++ internal/ai/real_provider_integration_test.go | 47 +++++++++++++++++++ internal/oss/aliyun_integration_test.go | 32 ++++++------- 3 files changed, 70 insertions(+), 17 deletions(-) create mode 100644 docs/evidence/real-ai-oss-one-shot-20261003.md create mode 100644 internal/ai/real_provider_integration_test.go diff --git a/docs/evidence/real-ai-oss-one-shot-20261003.md b/docs/evidence/real-ai-oss-one-shot-20261003.md new file mode 100644 index 0000000..82c52e6 --- /dev/null +++ b/docs/evidence/real-ai-oss-one-shot-20261003.md @@ -0,0 +1,8 @@ +# One authorized real-provider diagnostic — 2026-10-03 + +Scope: one real AI request and one new OSS object, using synthetic test data. **No call, customer audio, carrier, real SaaS, or production acceptance.** The user authorized this bounded test; no existing object was overwritten or deleted. + +- Existing private inputs: `.local/provider-ai.env` and `aliyun-oss.env`, both mode `0600`. Only required fields were mapped in process memory; credential values, signed URLs, object names, prompt/response text and raw provider errors were not printed or committed. These legacy files are **not** current approved SaaS task/provider snapshots or Dispatcher OSS JSON configuration. +- **AI:** one HTTPS OpenAI-compatible Bailian `qwen-plus` request through the current `ai.Binding.Complete` SDK path, with SDK retries disabled, an explicit 16-token limit and harmless synthetic text. Passed in 0.32 s; reply length 2 bytes, SHA-256 `565339bc4d33d72817b583024112eb7f5cdf3e5eef0252d6ec1b9c9a94e12bb3`. This checks real LLM credentials and connectivity only; it does **not** verify ASR, TTS, their compatibility with the current approved provider schema, or an actual call. +- **OSS:** one HTTPS presigned PUT using the existing official OSS v2 signer and Agent uploader. A cryptographically unique key under the isolated integration-test prefix prevented replacement of any previous object. The synthetic 27,648-byte payload was accepted, and the uploader confirmed local SHA-256 `c6fce42774195e84f761abfbb0d8cbb6a94c7a00d85479cd170faf819f6c22e2`. The test object is retained; no GET, deletion, real recording, or SaaS receipt was claimed. The test has a 45-second deadline and does not blindly retry an uncertain PUT. +- `make check` passed; business unit coverage **70.5%**. Its Mock checks do **not** run these separately opted-in external requests. Current real-call entry remains Mock-only, the current test host lacks tcpdump and ARI/HTTP configuration, and the required capture gate still forbids dialing. Historical `agent --call-once` and its ARI runtime were removed; this diagnostic does not restore them. diff --git a/internal/ai/real_provider_integration_test.go b/internal/ai/real_provider_integration_test.go new file mode 100644 index 0000000..39a891a --- /dev/null +++ b/internal/ai/real_provider_integration_test.go @@ -0,0 +1,47 @@ +package ai + +import ( + "context" + "crypto/sha256" + "net/url" + "os" + "strings" + "testing" + "time" + + "git.ipao.vip/rogee/go-sip/internal/configread" +) + +// This opt-in diagnostic checks one real provider SDK request. It is not a +// business call, an approved SaaS task, or evidence of ASR/TTS availability. +func TestRealBailianDiagnosticOnce(t *testing.T) { + if os.Getenv("AGENT_CALL_REAL_AI_INTEGRATION") != "1" { + t.Skip("real AI diagnostic requires explicit opt-in") + } + endpoint := os.Getenv("BAILIAN_BASE_URL") + credential := os.Getenv("BAILIAN_API_KEY") + parsed, err := url.Parse(endpoint) + if err != nil || parsed.Scheme != "https" || parsed.Host == "" || parsed.User != nil || parsed.RawQuery != "" || parsed.Fragment != "" || credential == "" { + t.Fatal("real AI diagnostic requires a private API key and plain HTTPS endpoint") + } + maxTokens := int64(16) + temperature := 0.0 + bound := Binding{ + Mode: "full_ai", + Prompt: "This is a harmless connectivity test. Respond briefly.", + LLM: &LLMConfig{ + Provider: configread.Provider{Endpoint: endpoint, Credential: credential}, + Model: "qwen-plus", Temperature: &temperature, MaxTokens: &maxTokens, Timeout: 30 * time.Second, + }, + } + ctx, cancel := context.WithTimeout(context.Background(), 35*time.Second) + defer cancel() + reply, err := bound.Complete(ctx, "Respond with OK.") + if err != nil { + t.Fatalf("real AI diagnostic failed: type=%T error_sha256=%x", err, sha256.Sum256([]byte(err.Error()))) + } + if strings.TrimSpace(reply) == "" { + t.Fatal("real AI diagnostic returned no text") + } + t.Logf("real LLM response: bytes=%d sha256=%x", len(reply), sha256.Sum256([]byte(reply))) +} diff --git a/internal/oss/aliyun_integration_test.go b/internal/oss/aliyun_integration_test.go index 70ef34f..0421e75 100644 --- a/internal/oss/aliyun_integration_test.go +++ b/internal/oss/aliyun_integration_test.go @@ -2,8 +2,10 @@ package oss_test import ( "context" + "crypto/rand" "crypto/sha256" "encoding/hex" + "errors" "os" "strings" "testing" @@ -29,15 +31,18 @@ func TestAlibabaOSSGrantPutIntegration(t *testing.T) { } client, err := ossclient.NewClient(config) if err != nil { - t.Fatal(err) + t.Fatal("OSS integration configuration is invalid") } + ctx, cancel := context.WithTimeout(context.Background(), 45*time.Second) + defer cancel() payload := []byte(strings.Repeat("agent-call-oss-integration\n", 1024)) digest := sha256.Sum256(payload) checksum := hex.EncodeToString(digest[:]) - objectKey := "agent-call/integration-tests/" + checksum + ".txt" - grant, err := client.Grant(context.Background(), "integration-"+checksum[:16], objectKey, checksum, int64(len(payload)), time.Now()) + uniqueID := "integration-" + rand.Text() + objectKey := config.KeyPrefix + "/" + uniqueID + "-" + checksum[:16] + ".txt" + grant, err := client.Grant(ctx, uniqueID, objectKey, checksum, int64(len(payload)), time.Now()) if err != nil { - t.Fatal(err) + t.Fatalf("OSS presign failed: type=%T error_sha256=%x", err, sha256.Sum256([]byte(err.Error()))) } file, err := os.CreateTemp(t.TempDir(), "oss-upload-*.txt") if err != nil { @@ -51,22 +56,15 @@ func TestAlibabaOSSGrantPutIntegration(t *testing.T) { t.Fatal(err) } uploader := agent.UploadClient{Now: time.Now, AllowedHosts: map[string]struct{}{}} - result, err := uploader.UploadFile(context.Background(), grant, file.Name()) + result, err := uploader.UploadFile(ctx, grant, file.Name()) if err != nil { - t.Fatalf("upload failed without exposing the presigned URL: %s", redactError(err, config.AccessKeyID)) + var httpErr *agent.UploadHTTPError + if errors.As(err, &httpErr) { + t.Fatalf("OSS PUT failed: HTTP %d", httpErr.StatusCode) + } + t.Fatalf("OSS PUT failed: type=%T error_sha256=%x", err, sha256.Sum256([]byte(err.Error()))) } if result.SizeBytes != int64(len(payload)) || !strings.EqualFold(result.SHA256, checksum) { t.Fatalf("upload result mismatch: %+v", result) } } - -func redactError(err error, accessKeyID string) string { - if err == nil { - return "" - } - message := err.Error() - if accessKeyID != "" { - message = strings.ReplaceAll(message, accessKeyID, "") - } - return message -}