mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-02-13 08:14:58 -05:00
otto suggestions
This commit is contained in:
@@ -104,6 +104,12 @@ TWITTER_CLIENT_SECRET=
|
||||
# Make a new workspace for your OAuth APP -- trust me
|
||||
# https://linear.app/settings/api/applications/new
|
||||
# Callback URL: http://localhost:3000/auth/integrations/oauth_callback
|
||||
LINEAR_API_KEY=
|
||||
# Linear project and team IDs for the feature request tracker.
|
||||
# Find these in your Linear workspace URL: linear.app/<workspace>/project/<project-id>
|
||||
# and in team settings. Used by the chat copilot to file and search feature requests.
|
||||
LINEAR_FEATURE_REQUEST_PROJECT_ID=
|
||||
LINEAR_FEATURE_REQUEST_TEAM_ID=
|
||||
LINEAR_CLIENT_ID=
|
||||
LINEAR_CLIENT_SECRET=
|
||||
|
||||
|
||||
@@ -17,14 +17,11 @@ from backend.api.features.chat.tools.models import (
|
||||
)
|
||||
from backend.blocks.linear._api import LinearClient
|
||||
from backend.data.model import APIKeyCredentials
|
||||
from backend.data.user import get_user_email_by_id
|
||||
from backend.util.settings import Settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Target project and team IDs in our Linear workspace
|
||||
FEATURE_REQUEST_PROJECT_ID = "13f066f3-f639-4a67-aaa3-31483ebdf8cd"
|
||||
TEAM_ID = "557fd3d5-087e-43a9-83e3-476c8313ce49"
|
||||
|
||||
MAX_SEARCH_RESULTS = 10
|
||||
|
||||
# GraphQL queries/mutations
|
||||
@@ -164,13 +161,16 @@ class SearchFeatureRequestsTool(BaseTool):
|
||||
)
|
||||
|
||||
try:
|
||||
secrets = _get_settings().secrets
|
||||
client = _get_linear_client()
|
||||
data = await client.query(
|
||||
SEARCH_ISSUES_QUERY,
|
||||
{
|
||||
"term": query,
|
||||
"filter": {
|
||||
"project": {"id": {"eq": FEATURE_REQUEST_PROJECT_ID}},
|
||||
"project": {
|
||||
"id": {"eq": secrets.linear_feature_request_project_id}
|
||||
},
|
||||
},
|
||||
"first": MAX_SEARCH_RESULTS,
|
||||
},
|
||||
@@ -261,14 +261,20 @@ class CreateFeatureRequestTool(BaseTool):
|
||||
return True
|
||||
|
||||
async def _find_or_create_customer(
|
||||
self, client: LinearClient, user_id: str
|
||||
self, client: LinearClient, user_id: str, name: str
|
||||
) -> dict:
|
||||
"""Find existing customer by user_id or create a new one via upsert."""
|
||||
"""Find existing customer by user_id or create a new one via upsert.
|
||||
|
||||
Args:
|
||||
client: Linear API client.
|
||||
user_id: Stable external ID used to deduplicate customers.
|
||||
name: Human-readable display name (e.g. the user's email).
|
||||
"""
|
||||
data = await client.mutate(
|
||||
CUSTOMER_UPSERT_MUTATION,
|
||||
{
|
||||
"input": {
|
||||
"name": user_id,
|
||||
"name": name,
|
||||
"externalId": user_id,
|
||||
},
|
||||
},
|
||||
@@ -304,6 +310,7 @@ class CreateFeatureRequestTool(BaseTool):
|
||||
)
|
||||
|
||||
try:
|
||||
secrets = _get_settings().secrets
|
||||
client = _get_linear_client()
|
||||
except Exception as e:
|
||||
logger.exception("Failed to create Linear client")
|
||||
@@ -313,9 +320,18 @@ class CreateFeatureRequestTool(BaseTool):
|
||||
session_id=session_id,
|
||||
)
|
||||
|
||||
# Resolve a human-readable name (email) for the Linear customer record.
|
||||
# Fall back to user_id if the lookup fails or returns None.
|
||||
try:
|
||||
customer_display_name = await get_user_email_by_id(user_id) or user_id
|
||||
except Exception:
|
||||
customer_display_name = user_id
|
||||
|
||||
# Step 1: Find or create customer for this user
|
||||
try:
|
||||
customer = await self._find_or_create_customer(client, user_id)
|
||||
customer = await self._find_or_create_customer(
|
||||
client, user_id, customer_display_name
|
||||
)
|
||||
customer_id = customer["id"]
|
||||
customer_name = customer["name"]
|
||||
except Exception as e:
|
||||
@@ -342,8 +358,8 @@ class CreateFeatureRequestTool(BaseTool):
|
||||
"input": {
|
||||
"title": title,
|
||||
"description": description,
|
||||
"teamId": TEAM_ID,
|
||||
"projectId": FEATURE_REQUEST_PROJECT_ID,
|
||||
"teamId": secrets.linear_feature_request_team_id,
|
||||
"projectId": secrets.linear_feature_request_project_id,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
@@ -18,6 +18,7 @@ from backend.api.features.chat.tools.models import (
|
||||
from ._test_data import make_session
|
||||
|
||||
_TEST_USER_ID = "test-user-feature-requests"
|
||||
_TEST_USER_EMAIL = "testuser@example.com"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -46,7 +47,7 @@ def _search_response(nodes: list[dict]) -> dict:
|
||||
|
||||
|
||||
def _customer_upsert_response(
|
||||
customer_id: str = "cust-1", name: str = _TEST_USER_ID, success: bool = True
|
||||
customer_id: str = "cust-1", name: str = _TEST_USER_EMAIL, success: bool = True
|
||||
) -> dict:
|
||||
return {
|
||||
"customerUpsert": {
|
||||
@@ -88,7 +89,7 @@ def _need_create_response(
|
||||
"need": {
|
||||
"id": need_id,
|
||||
"body": "description",
|
||||
"customer": {"id": "cust-1", "name": _TEST_USER_ID},
|
||||
"customer": {"id": "cust-1", "name": _TEST_USER_EMAIL},
|
||||
"issue": {
|
||||
"id": issue_id,
|
||||
"identifier": identifier,
|
||||
@@ -224,6 +225,15 @@ class TestSearchFeatureRequestsTool:
|
||||
class TestCreateFeatureRequestTool:
|
||||
"""Tests for CreateFeatureRequestTool._execute."""
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _patch_email_lookup(self):
|
||||
with patch(
|
||||
"backend.api.features.chat.tools.feature_requests.get_user_email_by_id",
|
||||
new_callable=AsyncMock,
|
||||
return_value=_TEST_USER_EMAIL,
|
||||
):
|
||||
yield
|
||||
|
||||
# ---- Happy paths -------------------------------------------------------
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
@@ -250,7 +260,7 @@ class TestCreateFeatureRequestTool:
|
||||
assert isinstance(resp, FeatureRequestCreatedResponse)
|
||||
assert resp.is_new_issue is True
|
||||
assert resp.issue_identifier == "FR-1"
|
||||
assert resp.customer_name == _TEST_USER_ID
|
||||
assert resp.customer_name == _TEST_USER_EMAIL
|
||||
assert client.mutate.call_count == 3
|
||||
|
||||
@pytest.mark.asyncio(loop_scope="session")
|
||||
|
||||
@@ -665,6 +665,14 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
|
||||
linear_api_key: str = Field(
|
||||
default="", description="Linear API key for system-level operations"
|
||||
)
|
||||
linear_feature_request_project_id: str = Field(
|
||||
default="",
|
||||
description="Linear project ID where feature requests are tracked",
|
||||
)
|
||||
linear_feature_request_team_id: str = Field(
|
||||
default="",
|
||||
description="Linear team ID used when creating feature request issues",
|
||||
)
|
||||
linear_client_id: str = Field(default="", description="Linear client ID")
|
||||
linear_client_secret: str = Field(default="", description="Linear client secret")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user