personal memory agent
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge branch 'hopper-nwbai4aw-simplify-vertex-settings'

+11 -132
+3 -26
apps/settings/routes.py
··· 449 449 "auth": auth, 450 450 "key_validation": key_validation, 451 451 "google_backend": providers_config.get("google_backend", "auto"), 452 - "vertex_project": providers_config.get("vertex_project", ""), 453 - "vertex_location": providers_config.get("vertex_location", ""), 454 452 "vertex_credentials_configured": vertex_creds_configured, 455 453 "vertex_credentials_email": vertex_creds_email, 456 454 } ··· 502 500 503 501 result = validate_vertex_credentials( 504 502 providers_config["vertex_credentials"], 505 - project=providers_config.get("vertex_project"), 506 - location=providers_config.get("vertex_location"), 507 503 ) 508 504 result["timestamp"] = datetime.now(timezone.utc).isoformat() 509 505 key_validation["google"] = result ··· 751 747 changed_fields["google_backend"] = {"old": old_val, "new": backend} 752 748 config["providers"]["google_backend"] = backend 753 749 754 - for vfield in ("vertex_project", "vertex_location"): 755 - if vfield in request_data: 756 - value = request_data[vfield] 757 - if not isinstance(value, str): 758 - return jsonify({"error": f"{vfield} must be a string"}), 400 759 - old_val = old_providers.get(vfield, "") 760 - if old_val != value: 761 - changed_fields[vfield] = {"old": old_val, "new": value} 762 - if value: 763 - config["providers"][vfield] = value 764 - else: 765 - config["providers"].pop(vfield, None) 766 - 767 750 # Handle vertex credentials 768 751 if "vertex_credentials" in request_data: 769 752 vertex_creds_value = request_data["vertex_credentials"] ··· 816 799 config["providers"]["vertex_credentials"] = creds_path_str 817 800 818 801 # Validate credentials by attempting to list models 819 - project = config["providers"].get("vertex_project") 820 - location = config["providers"].get("vertex_location") 821 - validation = validate_vertex_credentials( 822 - creds_path_str, 823 - project=project, 824 - location=location, 825 - ) 802 + validation = validate_vertex_credentials(creds_path_str) 826 803 827 804 if not validation.get("valid"): 828 - # Still save the file (user can fix project/location), but report the error 829 - # Don't block save - credentials may be valid once project is configured 805 + # Still save the file, but report the error 806 + # Don't block save - credentials may need reconfiguration 830 807 pass 831 808 832 809 # Store validation result
-39
apps/settings/workspace.html
··· 1926 1926 </div> 1927 1927 <div id="vertexFields" style="display:none"> 1928 1928 <div class="provider-row"> 1929 - <div class="settings-field"> 1930 - <label for="field-vertex-project">project</label> 1931 - <input type="text" id="field-vertex-project" placeholder="my-gcp-project"> 1932 - <small>Google Cloud project ID</small> 1933 - </div> 1934 - <div class="settings-field"> 1935 - <label for="field-vertex-location">location</label> 1936 - <input type="text" id="field-vertex-location" placeholder="Required — e.g., us-central1"> 1937 - <small>Google Cloud region</small> 1938 - </div> 1939 - </div> 1940 - <div class="provider-row"> 1941 1929 <div class="settings-field" style="flex:1" id="vertex-creds-field"> 1942 1930 <label>service account</label> 1943 1931 <div id="vertex-creds-status" style="margin-bottom:0.4em"> ··· 3770 3758 if (backendSelect) { 3771 3759 backendSelect.value = data.google_backend || 'auto'; 3772 3760 } 3773 - const vertexProjectField = document.getElementById('field-vertex-project'); 3774 - const vertexLocationField = document.getElementById('field-vertex-location'); 3775 - if (vertexProjectField) vertexProjectField.value = data.vertex_project || ''; 3776 - if (vertexLocationField) vertexLocationField.value = data.vertex_location || ''; 3777 3761 // Show/hide vertex fields 3778 3762 const vertexFields = document.getElementById('vertexFields'); 3779 3763 if (vertexFields) { ··· 4120 4104 showFieldStatus(this, 'error', err.message); 4121 4105 } 4122 4106 }); 4123 - 4124 - // Vertex project/location change handlers 4125 - for (const vfield of ['vertex-project', 'vertex-location']) { 4126 - const el = document.getElementById(`field-${vfield}`); 4127 - if (!el) continue; 4128 - el.addEventListener('change', async function() { 4129 - const key = vfield.replace('-', '_'); 4130 - try { 4131 - const response = await fetch('api/providers', { 4132 - method: 'PUT', 4133 - headers: { 'Content-Type': 'application/json' }, 4134 - body: JSON.stringify({ [key]: this.value }) 4135 - }); 4136 - const result = await response.json(); 4137 - if (result.error) throw new Error(result.error); 4138 - providersData = result; 4139 - showFieldStatus(this, 'saved'); 4140 - } catch (err) { 4141 - console.error(`Error saving ${vfield}:`, err); 4142 - showFieldStatus(this, 'error', err.message); 4143 - } 4144 - }); 4145 - } 4146 4107 4147 4108 // Vertex SA credentials handlers 4148 4109 function updateVertexCredsUI(configured, email) {
-5
tests/baselines/api/settings/providers.json
··· 443 443 "name": "google", 444 444 "vertex_env_keys": [ 445 445 "GOOGLE_GENAI_USE_VERTEXAI", 446 - "GOOGLE_CLOUD_PROJECT", 447 - "GOOGLE_CLOUD_LOCATION", 448 446 "GOOGLE_APPLICATION_CREDENTIALS" 449 447 ] 450 448 }, ··· 454 452 "name": "openai" 455 453 } 456 454 ], 457 - "vertex_credentials_email": null, 458 - "vertex_location": "", 459 - "vertex_project": "", 460 455 "vertex_credentials_configured": false, 461 456 "vertex_credentials_email": "" 462 457 }
-20
tests/test_cli_provider.py
··· 678 678 assert env["GOOGLE_APPLICATION_CREDENTIALS"] == "/tmp/fake-sa.json" 679 679 assert "GOOGLE_API_KEY" not in env 680 680 681 - def test_vertex_backend_with_project_location(self): 682 - """Vertex config sets project/location env vars.""" 683 - config = { 684 - "providers": { 685 - "google_backend": "vertex", 686 - "vertex_project": "my-project", 687 - "vertex_location": "us-central1", 688 - "auth": {"google": "api_key"}, 689 - } 690 - } 691 - with ( 692 - patch.dict(os.environ, {"GOOGLE_API_KEY": "gk-test"}, clear=True), 693 - patch("think.utils.get_config", return_value=config), 694 - ): 695 - env = build_cogitate_env("GOOGLE_API_KEY") 696 - assert env["GOOGLE_GENAI_USE_VERTEXAI"] == "true" 697 - assert env["GOOGLE_CLOUD_PROJECT"] == "my-project" 698 - assert env["GOOGLE_CLOUD_LOCATION"] == "us-central1" 699 - 700 681 def test_aistudio_backend_no_vertex_env_vars(self): 701 682 """AI Studio backend does not set Vertex env vars.""" 702 683 config = { ··· 740 721 env = build_cogitate_env("ANTHROPIC_API_KEY") 741 722 assert "GOOGLE_GENAI_USE_VERTEXAI" not in env 742 723 assert env["ANTHROPIC_API_KEY"] == "sk-ant" 743 -
+8 -19
tests/test_validate_key.py
··· 152 152 return_value=mock_creds, 153 153 ), 154 154 ): 155 - result = think.providers.google.validate_vertex_credentials( 156 - "/tmp/sa.json", project="my-proj", location="us-central1" 157 - ) 155 + result = think.providers.google.validate_vertex_credentials("/tmp/sa.json") 158 156 159 157 assert result == { 160 158 "valid": True, ··· 163 161 assert mock_cls.call_args.kwargs["vertexai"] is True 164 162 assert mock_cls.call_args.kwargs["credentials"] is mock_creds 165 163 assert "api_key" not in mock_cls.call_args.kwargs 166 - assert mock_cls.call_args.kwargs["project"] == "my-proj" 167 - assert mock_cls.call_args.kwargs["location"] == "us-central1" 164 + assert "project" not in mock_cls.call_args.kwargs 165 + assert "location" not in mock_cls.call_args.kwargs 168 166 169 167 170 168 def test_probe_backend_aistudio(): ··· 346 344 347 345 348 346 def test_providers_google_backend_roundtrip(settings_client): 349 - """PUT/GET google_backend, vertex_project, vertex_location.""" 347 + """PUT/GET google_backend.""" 350 348 client, journal = settings_client 351 349 352 350 response = client.put( 353 351 "/app/settings/api/providers", 354 - json={ 355 - "google_backend": "vertex", 356 - "vertex_project": "my-project", 357 - "vertex_location": "us-central1", 358 - }, 352 + json={"google_backend": "vertex"}, 359 353 ) 360 354 assert response.status_code == 200 361 355 payload = response.get_json() 362 356 assert payload["google_backend"] == "vertex" 363 - assert payload["vertex_project"] == "my-project" 364 - assert payload["vertex_location"] == "us-central1" 365 357 366 358 # Verify persisted 367 359 config = json.loads((journal / "config" / "journal.json").read_text()) 368 360 assert config["providers"]["google_backend"] == "vertex" 369 - assert config["providers"]["vertex_project"] == "my-project" 370 - assert config["providers"]["vertex_location"] == "us-central1" 371 361 372 362 # GET returns the same 373 363 response = client.get("/app/settings/api/providers") 374 364 payload = response.get_json() 375 365 assert payload["google_backend"] == "vertex" 376 - assert payload["vertex_project"] == "my-project" 377 - assert payload["vertex_location"] == "us-central1" 378 366 379 367 380 368 def test_providers_vertex_credentials_roundtrip(settings_client): ··· 488 476 config["providers"]["vertex_credentials"] = str( 489 477 journal / "config" / "vertex-credentials.json" 490 478 ) 491 - config["providers"]["vertex_project"] = "my-project" 492 479 config_path.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8") 493 480 494 481 with patch( ··· 501 488 payload = response.get_json() 502 489 assert payload["key_validation"]["google"]["valid"] is True 503 490 assert payload["key_validation"]["google"]["backend"] == "vertex" 504 - mock_validate.assert_called_once() 491 + mock_validate.assert_called_once_with( 492 + str(journal / "config" / "vertex-credentials.json") 493 + )
-2
think/providers/__init__.py
··· 52 52 "env_key": "GOOGLE_API_KEY", 53 53 "vertex_env_keys": [ 54 54 "GOOGLE_GENAI_USE_VERTEXAI", 55 - "GOOGLE_CLOUD_PROJECT", 56 - "GOOGLE_CLOUD_LOCATION", 57 55 "GOOGLE_APPLICATION_CREDENTIALS", 58 56 ], 59 57 },
-9
think/providers/cli.py
··· 471 471 if creds_path and os.path.exists(creds_path): 472 472 env["GOOGLE_APPLICATION_CREDENTIALS"] = creds_path 473 473 # else: GOOGLE_APPLICATION_CREDENTIALS may be inherited from env 474 - # Set project/location from config if present 475 - vertex_project = providers_config.get("vertex_project") 476 - if vertex_project: 477 - env["GOOGLE_CLOUD_PROJECT"] = vertex_project 478 - vertex_location = providers_config.get("vertex_location") 479 - if vertex_location: 480 - env["GOOGLE_CLOUD_LOCATION"] = vertex_location 481 474 else: 482 475 # AI Studio: clear any inherited Vertex env vars so the CLI 483 476 # doesn't accidentally run in Vertex mode. 484 477 for vkey in ( 485 478 "GOOGLE_APPLICATION_CREDENTIALS", 486 479 "GOOGLE_GENAI_USE_VERTEXAI", 487 - "GOOGLE_CLOUD_PROJECT", 488 - "GOOGLE_CLOUD_LOCATION", 489 480 ): 490 481 env.pop(vkey, None) 491 482 return env
-12
think/providers/google.py
··· 147 147 148 148 if backend == "vertex": 149 149 creds_path = providers_config.get("vertex_credentials") 150 - project = providers_config.get("vertex_project") 151 - location = providers_config.get("vertex_location") 152 150 153 151 client_kwargs: dict[str, Any] = { 154 152 "vertexai": True, 155 153 "http_options": http_options, 156 154 } 157 - if project: 158 - client_kwargs["project"] = project 159 - if location: 160 - client_kwargs["location"] = location 161 155 162 156 if creds_path and os.path.exists(creds_path): 163 157 from google.oauth2.service_account import Credentials ··· 801 795 802 796 def validate_vertex_credentials( 803 797 creds_path: str, 804 - project: str | None = None, 805 - location: str | None = None, 806 798 ) -> dict: 807 799 """Validate Vertex AI service account credentials by listing models. 808 800 ··· 822 814 "credentials": creds, 823 815 "http_options": types.HttpOptions(timeout=10000), 824 816 } 825 - if project: 826 - client_kwargs["project"] = project 827 - if location: 828 - client_kwargs["location"] = location 829 817 830 818 client = genai.Client(**client_kwargs) 831 819 list(client.models.list(config={"page_size": 1}))