Skip to content

feat: Together AI detection, 212 unit tests, v3 docs#110

Merged
Pyronewbic merged 1 commit into
mainfrom
dev
May 15, 2026
Merged

feat: Together AI detection, 212 unit tests, v3 docs#110
Pyronewbic merged 1 commit into
mainfrom
dev

Conversation

@Pyronewbic

Copy link
Copy Markdown
Owner

Summary

  • Together AI provider for card detection (GLM-4.6V-Flash) with automatic fallback to Claude Sonnet
  • Extracted response parsers as testable pure functions (parseAnthropicResponse, parseTogetherResponse)
  • Exported validateAndShape, buildSignal, deriveEra for mock-based testing
  • 40 new unit tests: API response parsing, grade validation, PSA signal edge cases, era classification, bounds parsing
  • TOGETHER_API_KEY added to Terraform secrets + .env.example
  • Practices skill updated for Sonnet detection

Test plan

  • node test/unit-test.js — 212 pass
  • CI unit + codeql pass
  • Verify detection falls back to Claude when no TOGETHER_API_KEY

- Card detection routes to Together AI (GLM-4.6V-Flash) when TOGETHER_API_KEY set, falls back to Claude Sonnet
- Extracted parseAnthropicResponse/parseTogetherResponse as testable pure functions
- Exported validateAndShape, buildSignal, deriveEra for mock testing
- 40 new unit tests: API response parsing, grade validation, PSA signal, era classification, bounds parsing
- Added TOGETHER_API_KEY to terraform secrets + .env.example
Comment on lines +54 to +58
}

await validateImageUrl(imageUrl);
const imgRes = await axios.get(imageUrl, {
responseType: "arraybuffer",
timeout: 15_000,
maxRedirects: 5,
});
const imgBuf = Buffer.from(imgRes.data);
const { width, height } = await sharp(imgBuf).metadata();
if (!width || !height) return { imageUrl, cropped: false };
export function parseTogetherResponse(data) {
const text = data?.choices?.[0]?.message?.content || "";
const usage = data?.usage || {};
@github-actions

Copy link
Copy Markdown

Terraform Plan

google_project_service.secretmanager: Refreshing state... [id=casecomp-495718/secretmanager.googleapis.com]
google_project_service.compute: Refreshing state... [id=casecomp-495718/compute.googleapis.com]
google_project_service.binaryauthorization: Refreshing state... [id=casecomp-495718/binaryauthorization.googleapis.com]
google_logging_metric.api_errors: Refreshing state... [id=cardscrapebot-errors]
data.google_project.current: Reading...
google_project_service.monitoring: Refreshing state... [id=casecomp-495718/monitoring.googleapis.com]
google_project_service.firestore: Refreshing state... [id=casecomp-495718/firestore.googleapis.com]
data.google_secret_manager_secret_version.api_key: Reading...
google_project_service.scheduler: Refreshing state... [id=casecomp-495718/cloudscheduler.googleapis.com]
google_storage_bucket.site: Refreshing state... [id=casecomp-site]
google_project_service.containeranalysis: Refreshing state... [id=casecomp-495718/containeranalysis.googleapis.com]
data.google_secret_manager_secret_version.api_key: Read complete after 0s [id=projects/129850122606/secrets/CASECOMP_API_KEY/versions/1]
google_project_service.cloudbuild: Refreshing state... [id=casecomp-495718/cloudbuild.googleapis.com]
data.google_project.current: Read complete after 0s [id=projects/casecomp-495718]
google_project_service.run: Refreshing state... [id=casecomp-495718/run.googleapis.com]
google_compute_managed_ssl_certificate.api_cert: Refreshing state... [id=projects/casecomp-495718/global/sslCertificates/cardscrapebot-cert-v2]
google_compute_managed_ssl_certificate.site_cert: Refreshing state... [id=projects/casecomp-495718/global/sslCertificates/casecomp-site-cert]
google_storage_bucket_iam_member.site_public: Refreshing state... [id=b/casecomp-site/roles/storage.objectViewer/allUsers]
google_compute_global_address.api_ip: Refreshing state... [id=projects/casecomp-495718/global/addresses/cardscrapebot-ip]
google_monitoring_notification_channel.email: Refreshing state... [id=projects/casecomp-495718/notificationChannels/3431772178774051140]
google_firestore_database.default: Refreshing state... [id=projects/casecomp-495718/databases/(default)]
google_binary_authorization_policy.default: Refreshing state... [id=projects/casecomp-495718]
google_monitoring_uptime_check_config.api_uptime: Refreshing state... [id=projects/casecomp-495718/uptimeCheckConfigs/casecomp-api-health-lQkUaC0Vzb8]
google_secret_manager_secret.api_secrets["EBAY_CLIENT_ID"]: Refreshing state... [id=projects/casecomp-495718/secrets/EBAY_CLIENT_ID]
google_cloud_scheduler_job.check_alerts: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/jobs/casecomp-check-alerts]
google_secret_manager_secret.api_secrets["EBAY_CLIENT_SECRET"]: Refreshing state... [id=projects/casecomp-495718/secrets/EBAY_CLIENT_SECRET]
google_secret_manager_secret.api_secrets["PSA_AUTH_TOKEN"]: Refreshing state... [id=projects/casecomp-495718/secrets/PSA_AUTH_TOKEN]
google_secret_manager_secret.api_secrets["CASECOMP_SANDBOX_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_SANDBOX_KEY]
google_secret_manager_secret.api_secrets["CASECOMP_ADMIN_SUB"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_ADMIN_SUB]
google_secret_manager_secret.api_secrets["CASECOMP_JWT_SECRET"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_JWT_SECRET]
google_secret_manager_secret.api_secrets["CASECOMP_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_API_KEY]
google_secret_manager_secret.api_secrets["RESEND_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/RESEND_API_KEY]
google_secret_manager_secret.api_secrets["GOOGLE_OAUTH_CLIENT_ID"]: Refreshing state... [id=projects/casecomp-495718/secrets/GOOGLE_OAUTH_CLIENT_ID]
google_secret_manager_secret.api_secrets["ANTHROPIC_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/ANTHROPIC_API_KEY]
google_cloud_scheduler_job.track_prices: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/jobs/casecomp-track-prices]
google_cloud_run_v2_service.site["us-central1"]: Refreshing state... [id=projects/casecomp-495718/locations/us-central1/services/casecomp-site]
google_cloud_run_v2_service.site["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/services/casecomp-site]
google_monitoring_alert_policy.api_error_alert: Refreshing state... [id=projects/casecomp-495718/alertPolicies/16365448047387079183]
google_monitoring_alert_policy.api_uptime_alert: Refreshing state... [id=projects/casecomp-495718/alertPolicies/14098674883088940398]
google_secret_manager_secret_iam_member.cloud_run_access["ANTHROPIC_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/ANTHROPIC_API_KEY/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["PSA_AUTH_TOKEN"]: Refreshing state... [id=projects/casecomp-495718/secrets/PSA_AUTH_TOKEN/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["CASECOMP_ADMIN_SUB"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_ADMIN_SUB/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["RESEND_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/RESEND_API_KEY/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["CASECOMP_JWT_SECRET"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_JWT_SECRET/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["GOOGLE_OAUTH_CLIENT_ID"]: Refreshing state... [id=projects/casecomp-495718/secrets/GOOGLE_OAUTH_CLIENT_ID/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["CASECOMP_API_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_API_KEY/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["CASECOMP_SANDBOX_KEY"]: Refreshing state... [id=projects/casecomp-495718/secrets/CASECOMP_SANDBOX_KEY/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["EBAY_CLIENT_ID"]: Refreshing state... [id=projects/casecomp-495718/secrets/EBAY_CLIENT_ID/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_secret_manager_secret_iam_member.cloud_run_access["EBAY_CLIENT_SECRET"]: Refreshing state... [id=projects/casecomp-495718/secrets/EBAY_CLIENT_SECRET/roles/secretmanager.secretAccessor/serviceAccount:129850122606-compute@developer.gserviceaccount.com]
google_cloud_run_v2_service.api["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/services/casecomp-api]
google_cloud_run_v2_service.api["us-central1"]: Refreshing state... [id=projects/casecomp-495718/locations/us-central1/services/casecomp-api]
google_compute_region_network_endpoint_group.api_neg["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/regions/asia-south1/networkEndpointGroups/casecomp-api-neg]
google_cloud_run_v2_service_iam_member.public["us-central1"]: Refreshing state... [id=projects/casecomp-495718/locations/us-central1/services/casecomp-api/roles/run.invoker/allUsers]
google_compute_region_network_endpoint_group.api_neg["us-central1"]: Refreshing state... [id=projects/casecomp-495718/regions/us-central1/networkEndpointGroups/casecomp-api-neg-us-central1]
google_cloud_run_v2_service_iam_member.public["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/services/casecomp-api/roles/run.invoker/allUsers]
google_cloud_run_v2_service_iam_member.site_public["us-central1"]: Refreshing state... [id=projects/casecomp-495718/locations/us-central1/services/casecomp-site/roles/run.invoker/allUsers]
google_cloud_run_v2_service_iam_member.site_public["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/locations/asia-south1/services/casecomp-site/roles/run.invoker/allUsers]
google_compute_region_network_endpoint_group.site_neg["us-central1"]: Refreshing state... [id=projects/casecomp-495718/regions/us-central1/networkEndpointGroups/casecomp-site-neg-us-central1]
google_compute_region_network_endpoint_group.site_neg["asia-south1"]: Refreshing state... [id=projects/casecomp-495718/regions/asia-south1/networkEndpointGroups/casecomp-site-neg]
google_compute_backend_service.api_backend: Refreshing state... [id=projects/casecomp-495718/global/backendServices/cardscrapebot-backend]
google_compute_backend_service.site_backend: Refreshing state... [id=projects/casecomp-495718/global/backendServices/casecomp-site-backend]
google_compute_url_map.api_urlmap: Refreshing state... [id=projects/casecomp-495718/global/urlMaps/cardscrapebot-urlmap]
google_compute_target_https_proxy.api_proxy: Refreshing state... [id=projects/casecomp-495718/global/targetHttpsProxies/cardscrapebot-https-proxy]
google_compute_global_forwarding_rule.api_https: Refreshing state... [id=projects/casecomp-495718/global/forwardingRules/cardscrapebot-https-rule]

Terraform used the selected providers to generate the following execution
plan. Resource actions are indicated with the following symbols:
  + create
  ~ update in-place

Terraform will perform the following actions:

  # google_cloud_run_v2_service.api["asia-south1"] will be updated in-place
  ~ resource "google_cloud_run_v2_service" "api" {
        id                      = "projects/casecomp-495718/locations/asia-south1/services/casecomp-api"
        name                    = "casecomp-api"
        # (30 unchanged attributes hidden)

      ~ template {
            # (9 unchanged attributes hidden)

          ~ containers {
                name        = null
                # (5 unchanged attributes hidden)

              + env {
                  + name = "TOGETHER_API_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "TOGETHER_API_KEY"
                          + version = "latest"
                        }
                    }
                }

                # (14 unchanged blocks hidden)
            }

            # (1 unchanged block hidden)
        }

        # (2 unchanged blocks hidden)
    }

  # google_cloud_run_v2_service.api["us-central1"] will be updated in-place
  ~ resource "google_cloud_run_v2_service" "api" {
        id                      = "projects/casecomp-495718/locations/us-central1/services/casecomp-api"
        name                    = "casecomp-api"
        # (30 unchanged attributes hidden)

      ~ template {
            # (9 unchanged attributes hidden)

          ~ containers {
                name        = null
                # (5 unchanged attributes hidden)

              + env {
                  + name = "TOGETHER_API_KEY"

                  + value_source {
                      + secret_key_ref {
                          + secret  = "TOGETHER_API_KEY"
                          + version = "latest"
                        }
                    }
                }

                # (14 unchanged blocks hidden)
            }

            # (1 unchanged block hidden)
        }

        # (2 unchanged blocks hidden)
    }

  # google_secret_manager_secret.api_secrets["TOGETHER_API_KEY"] will be created
  + resource "google_secret_manager_secret" "api_secrets" {
      + create_time           = (known after apply)
      + effective_annotations = (known after apply)
      + effective_labels      = (known after apply)
      + expire_time           = (known after apply)
      + id                    = (known after apply)
      + name                  = (known after apply)
      + project               = "casecomp-495718"
      + secret_id             = "TOGETHER_API_KEY"
      + terraform_labels      = (known after apply)

      + replication {
          + auto {
            }
        }
    }

  # google_secret_manager_secret_iam_member.cloud_run_access["TOGETHER_API_KEY"] will be created
  + resource "google_secret_manager_secret_iam_member" "cloud_run_access" {
      + etag      = (known after apply)
      + id        = (known after apply)
      + member    = "serviceAccount:129850122606-compute@developer.gserviceaccount.com"
      + project   = (known after apply)
      + role      = "roles/secretmanager.secretAccessor"
      + secret_id = (known after apply)
    }

Plan: 2 to add, 2 to change, 0 to destroy.

─────────────────────────────────────────────────────────────────────────────

Saved the plan to: tfplan

To perform exactly these actions, run the following command to apply:
    terraform apply "tfplan"

Merge to main to apply.

@Pyronewbic Pyronewbic merged commit d9fa04e into main May 15, 2026
16 of 17 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants