shotgun-sh 0.1.16.dev2__py3-none-any.whl → 0.2.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of shotgun-sh might be problematic. Click here for more details.

Files changed (55) hide show
  1. shotgun/agents/common.py +4 -5
  2. shotgun/agents/config/constants.py +23 -6
  3. shotgun/agents/config/manager.py +239 -76
  4. shotgun/agents/config/models.py +74 -84
  5. shotgun/agents/config/provider.py +174 -85
  6. shotgun/agents/history/compaction.py +1 -1
  7. shotgun/agents/history/history_processors.py +18 -9
  8. shotgun/agents/history/token_counting/__init__.py +31 -0
  9. shotgun/agents/history/token_counting/anthropic.py +89 -0
  10. shotgun/agents/history/token_counting/base.py +67 -0
  11. shotgun/agents/history/token_counting/openai.py +80 -0
  12. shotgun/agents/history/token_counting/sentencepiece_counter.py +119 -0
  13. shotgun/agents/history/token_counting/tokenizer_cache.py +90 -0
  14. shotgun/agents/history/token_counting/utils.py +147 -0
  15. shotgun/agents/history/token_estimation.py +12 -12
  16. shotgun/agents/llm.py +62 -0
  17. shotgun/agents/models.py +2 -2
  18. shotgun/agents/tools/web_search/__init__.py +42 -15
  19. shotgun/agents/tools/web_search/anthropic.py +54 -50
  20. shotgun/agents/tools/web_search/gemini.py +31 -20
  21. shotgun/agents/tools/web_search/openai.py +4 -4
  22. shotgun/build_constants.py +2 -2
  23. shotgun/cli/config.py +34 -63
  24. shotgun/cli/feedback.py +4 -2
  25. shotgun/cli/models.py +2 -2
  26. shotgun/codebase/core/ingestor.py +47 -8
  27. shotgun/codebase/core/manager.py +7 -3
  28. shotgun/codebase/models.py +4 -4
  29. shotgun/llm_proxy/__init__.py +16 -0
  30. shotgun/llm_proxy/clients.py +39 -0
  31. shotgun/llm_proxy/constants.py +8 -0
  32. shotgun/main.py +6 -0
  33. shotgun/posthog_telemetry.py +15 -11
  34. shotgun/sentry_telemetry.py +3 -3
  35. shotgun/shotgun_web/__init__.py +19 -0
  36. shotgun/shotgun_web/client.py +138 -0
  37. shotgun/shotgun_web/constants.py +17 -0
  38. shotgun/shotgun_web/models.py +47 -0
  39. shotgun/telemetry.py +7 -4
  40. shotgun/tui/app.py +26 -8
  41. shotgun/tui/screens/chat.py +2 -8
  42. shotgun/tui/screens/chat_screen/command_providers.py +118 -11
  43. shotgun/tui/screens/chat_screen/history.py +3 -1
  44. shotgun/tui/screens/feedback.py +2 -2
  45. shotgun/tui/screens/model_picker.py +327 -0
  46. shotgun/tui/screens/provider_config.py +118 -28
  47. shotgun/tui/screens/shotgun_auth.py +295 -0
  48. shotgun/tui/screens/welcome.py +176 -0
  49. shotgun/utils/env_utils.py +12 -0
  50. {shotgun_sh-0.1.16.dev2.dist-info → shotgun_sh-0.2.1.dist-info}/METADATA +2 -2
  51. {shotgun_sh-0.1.16.dev2.dist-info → shotgun_sh-0.2.1.dist-info}/RECORD +54 -37
  52. shotgun/agents/history/token_counting.py +0 -429
  53. {shotgun_sh-0.1.16.dev2.dist-info → shotgun_sh-0.2.1.dist-info}/WHEEL +0 -0
  54. {shotgun_sh-0.1.16.dev2.dist-info → shotgun_sh-0.2.1.dist-info}/entry_points.txt +0 -0
  55. {shotgun_sh-0.1.16.dev2.dist-info → shotgun_sh-0.2.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,138 @@
1
+ """HTTP client for Shotgun Web API."""
2
+
3
+ import httpx
4
+
5
+ from shotgun.logging_config import get_logger
6
+
7
+ from .constants import (
8
+ SHOTGUN_WEB_BASE_URL,
9
+ UNIFICATION_TOKEN_CREATE_PATH,
10
+ UNIFICATION_TOKEN_STATUS_PATH,
11
+ )
12
+ from .models import (
13
+ TokenCreateRequest,
14
+ TokenCreateResponse,
15
+ TokenStatusResponse,
16
+ )
17
+
18
+ logger = get_logger(__name__)
19
+
20
+
21
+ class ShotgunWebClient:
22
+ """HTTP client for Shotgun Web API."""
23
+
24
+ def __init__(self, base_url: str | None = None, timeout: float = 10.0):
25
+ """Initialize Shotgun Web client.
26
+
27
+ Args:
28
+ base_url: Base URL for Shotgun Web API. If None, uses SHOTGUN_WEB_BASE_URL
29
+ timeout: Request timeout in seconds
30
+ """
31
+ self.base_url = base_url or SHOTGUN_WEB_BASE_URL
32
+ self.timeout = timeout
33
+
34
+ def create_unification_token(self, shotgun_instance_id: str) -> TokenCreateResponse:
35
+ """Create a unification token for CLI authentication.
36
+
37
+ Args:
38
+ shotgun_instance_id: UUID for this shotgun instance
39
+
40
+ Returns:
41
+ Token creation response with token and auth URL
42
+
43
+ Raises:
44
+ httpx.HTTPError: If request fails
45
+ """
46
+ url = f"{self.base_url}{UNIFICATION_TOKEN_CREATE_PATH}"
47
+ request_data = TokenCreateRequest(shotgun_instance_id=shotgun_instance_id)
48
+
49
+ logger.debug("Creating unification token for instance %s", shotgun_instance_id)
50
+
51
+ try:
52
+ response = httpx.post(
53
+ url,
54
+ json=request_data.model_dump(),
55
+ timeout=self.timeout,
56
+ )
57
+ response.raise_for_status()
58
+
59
+ data = response.json()
60
+ result = TokenCreateResponse.model_validate(data)
61
+
62
+ logger.info(
63
+ "Successfully created unification token, expires in %d seconds",
64
+ result.expires_in_seconds,
65
+ )
66
+ return result
67
+
68
+ except httpx.HTTPError as e:
69
+ logger.error("Failed to create unification token: %s", e)
70
+ raise
71
+
72
+ def check_token_status(self, token: str) -> TokenStatusResponse:
73
+ """Check token status and get keys if completed.
74
+
75
+ Args:
76
+ token: Unification token to check
77
+
78
+ Returns:
79
+ Token status response with status and keys (if completed)
80
+
81
+ Raises:
82
+ httpx.HTTPStatusError: If token not found (404) or expired (410)
83
+ httpx.HTTPError: For other request failures
84
+ """
85
+ url = f"{self.base_url}{UNIFICATION_TOKEN_STATUS_PATH.format(token=token)}"
86
+
87
+ logger.debug("Checking status for token %s...", token[:8])
88
+
89
+ try:
90
+ response = httpx.get(url, timeout=self.timeout)
91
+ response.raise_for_status()
92
+
93
+ data = response.json()
94
+ result = TokenStatusResponse.model_validate(data)
95
+
96
+ logger.debug("Token status: %s", result.status)
97
+ return result
98
+
99
+ except httpx.HTTPStatusError as e:
100
+ if e.response.status_code == 404:
101
+ logger.error("Token not found: %s", token[:8])
102
+ elif e.response.status_code == 410:
103
+ logger.error("Token expired: %s", token[:8])
104
+ raise
105
+ except httpx.HTTPError as e:
106
+ logger.error("Failed to check token status: %s", e)
107
+ raise
108
+
109
+
110
+ # Convenience functions for standalone use
111
+ def create_unification_token(shotgun_instance_id: str) -> TokenCreateResponse:
112
+ """Create a unification token.
113
+
114
+ Convenience function that creates a client and calls create_unification_token.
115
+
116
+ Args:
117
+ shotgun_instance_id: UUID for this shotgun instance
118
+
119
+ Returns:
120
+ Token creation response
121
+ """
122
+ client = ShotgunWebClient()
123
+ return client.create_unification_token(shotgun_instance_id)
124
+
125
+
126
+ def check_token_status(token: str) -> TokenStatusResponse:
127
+ """Check token status.
128
+
129
+ Convenience function that creates a client and calls check_token_status.
130
+
131
+ Args:
132
+ token: Unification token to check
133
+
134
+ Returns:
135
+ Token status response
136
+ """
137
+ client = ShotgunWebClient()
138
+ return client.check_token_status(token)
@@ -0,0 +1,17 @@
1
+ """Constants for Shotgun Web API."""
2
+
3
+ import os
4
+
5
+ # Shotgun Web API base URL
6
+ # Default to production URL, can be overridden with environment variable
7
+ SHOTGUN_WEB_BASE_URL = os.environ.get(
8
+ "SHOTGUN_WEB_BASE_URL", "https://api-701197220809.us-east1.run.app"
9
+ )
10
+
11
+ # API endpoints
12
+ UNIFICATION_TOKEN_CREATE_PATH = "/api/unification/token/create" # noqa: S105
13
+ UNIFICATION_TOKEN_STATUS_PATH = "/api/unification/token/{token}/status" # noqa: S105
14
+
15
+ # Polling configuration
16
+ DEFAULT_POLL_INTERVAL_SECONDS = 3
17
+ DEFAULT_TOKEN_TIMEOUT_SECONDS = 1800 # 30 minutes
@@ -0,0 +1,47 @@
1
+ """Pydantic models for Shotgun Web API."""
2
+
3
+ from enum import StrEnum
4
+
5
+ from pydantic import BaseModel, Field
6
+
7
+
8
+ class TokenStatus(StrEnum):
9
+ """Token status enum matching API specification."""
10
+
11
+ PENDING = "pending"
12
+ COMPLETED = "completed"
13
+ AWAITING_PAYMENT = "awaiting_payment"
14
+ EXPIRED = "expired"
15
+
16
+
17
+ class TokenCreateRequest(BaseModel):
18
+ """Request model for creating a unification token."""
19
+
20
+ shotgun_instance_id: str = Field(
21
+ description="CLI-provided UUID for shotgun instance"
22
+ )
23
+
24
+
25
+ class TokenCreateResponse(BaseModel):
26
+ """Response model for token creation."""
27
+
28
+ token: str = Field(description="Secure authentication token")
29
+ auth_url: str = Field(description="Web authentication URL for user to complete")
30
+ expires_in_seconds: int = Field(description="Token expiration time in seconds")
31
+
32
+
33
+ class TokenStatusResponse(BaseModel):
34
+ """Response model for token status check."""
35
+
36
+ status: TokenStatus = Field(description="Current token status")
37
+ supabase_key: str | None = Field(
38
+ default=None,
39
+ description="Supabase user JWT (only returned when status=completed)",
40
+ )
41
+ litellm_key: str | None = Field(
42
+ default=None,
43
+ description="LiteLLM virtual key (only returned when status=completed)",
44
+ )
45
+ message: str | None = Field(
46
+ default=None, description="Human-readable status message"
47
+ )
shotgun/telemetry.py CHANGED
@@ -72,12 +72,15 @@ def setup_logfire_observability() -> bool:
72
72
  from shotgun.agents.config import get_config_manager
73
73
 
74
74
  config_manager = get_config_manager()
75
- user_id = config_manager.get_user_id()
75
+ shotgun_instance_id = config_manager.get_shotgun_instance_id()
76
76
 
77
- # Set user_id as baggage in global context - this will be included in all logs/spans
78
- ctx = baggage.set_baggage("user_id", user_id)
77
+ # Set shotgun_instance_id as baggage in global context - this will be included in all logs/spans
78
+ ctx = baggage.set_baggage("shotgun_instance_id", shotgun_instance_id)
79
79
  context.attach(ctx)
80
- logger.debug("Logfire user context set with user_id: %s", user_id)
80
+ logger.debug(
81
+ "Logfire user context set with shotgun_instance_id: %s",
82
+ shotgun_instance_id,
83
+ )
81
84
  except Exception as e:
82
85
  logger.warning("Failed to set Logfire user context: %s", e)
83
86
 
shotgun/tui/app.py CHANGED
@@ -8,13 +8,16 @@ from textual.screen import Screen
8
8
  from shotgun.agents.config import ConfigManager, get_config_manager
9
9
  from shotgun.logging_config import get_logger
10
10
  from shotgun.tui.screens.splash import SplashScreen
11
+ from shotgun.utils.env_utils import is_shotgun_account_enabled
11
12
  from shotgun.utils.file_system_utils import get_shotgun_base_path
12
13
  from shotgun.utils.update_checker import perform_auto_update_async
13
14
 
14
15
  from .screens.chat import ChatScreen
15
16
  from .screens.directory_setup import DirectorySetupScreen
16
17
  from .screens.feedback import FeedbackScreen
18
+ from .screens.model_picker import ModelPickerScreen
17
19
  from .screens.provider_config import ProviderConfigScreen
20
+ from .screens.welcome import WelcomeScreen
18
21
 
19
22
  logger = get_logger(__name__)
20
23
 
@@ -23,6 +26,7 @@ class ShotgunApp(App[None]):
23
26
  SCREENS = {
24
27
  "chat": ChatScreen,
25
28
  "provider_config": ProviderConfigScreen,
29
+ "model_picker": ModelPickerScreen,
26
30
  "directory_setup": DirectorySetupScreen,
27
31
  "feedback": FeedbackScreen,
28
32
  }
@@ -58,20 +62,34 @@ class ShotgunApp(App[None]):
58
62
  def refresh_startup_screen(self) -> None:
59
63
  """Push the appropriate screen based on configured providers."""
60
64
  if not self.config_manager.has_any_provider_key():
61
- if isinstance(self.screen, ProviderConfigScreen):
65
+ # If Shotgun Account is enabled, show welcome screen with choice
66
+ # Otherwise, go directly to provider config (BYOK only)
67
+ if is_shotgun_account_enabled():
68
+ if isinstance(self.screen, WelcomeScreen):
69
+ return
70
+
71
+ self.push_screen(
72
+ WelcomeScreen(),
73
+ callback=lambda _arg: self.refresh_startup_screen(),
74
+ )
75
+ return
76
+ else:
77
+ if isinstance(self.screen, ProviderConfigScreen):
78
+ return
79
+
80
+ self.push_screen(
81
+ ProviderConfigScreen(),
82
+ callback=lambda _arg: self.refresh_startup_screen(),
83
+ )
62
84
  return
63
-
64
- self.push_screen(
65
- "provider_config", callback=lambda _arg: self.refresh_startup_screen()
66
- )
67
- return
68
85
 
69
86
  if not self.check_local_shotgun_directory_exists():
70
87
  if isinstance(self.screen, DirectorySetupScreen):
71
88
  return
72
89
 
73
90
  self.push_screen(
74
- "directory_setup", callback=lambda _arg: self.refresh_startup_screen()
91
+ DirectorySetupScreen(),
92
+ callback=lambda _arg: self.refresh_startup_screen(),
75
93
  )
76
94
  return
77
95
 
@@ -108,7 +126,7 @@ class ShotgunApp(App[None]):
108
126
  submit_feedback_survey(feedback)
109
127
  self.notify("Feedback sent. Thank you!")
110
128
 
111
- self.push_screen("feedback", callback=handle_feedback)
129
+ self.push_screen(FeedbackScreen(), callback=handle_feedback)
112
130
 
113
131
 
114
132
  def run(no_update_check: bool = False, continue_session: bool = False) -> None:
@@ -54,11 +54,8 @@ from ..components.prompt_input import PromptInput
54
54
  from ..components.spinner import Spinner
55
55
  from ..utils.mode_progress import PlaceholderHints
56
56
  from .chat_screen.command_providers import (
57
- AgentModeProvider,
58
- CodebaseCommandProvider,
59
57
  DeleteCodebasePaletteProvider,
60
- ProviderSetupProvider,
61
- UsageProvider,
58
+ UnifiedCommandProvider,
62
59
  )
63
60
 
64
61
  logger = logging.getLogger(__name__)
@@ -233,10 +230,7 @@ class ChatScreen(Screen[None]):
233
230
  ]
234
231
 
235
232
  COMMANDS = {
236
- AgentModeProvider,
237
- ProviderSetupProvider,
238
- CodebaseCommandProvider,
239
- UsageProvider,
233
+ UnifiedCommandProvider,
240
234
  }
241
235
 
242
236
  value = reactive("")
@@ -5,6 +5,8 @@ from textual.command import DiscoveryHit, Hit, Provider
5
5
 
6
6
  from shotgun.agents.models import AgentType
7
7
  from shotgun.codebase.models import CodebaseGraph
8
+ from shotgun.tui.screens.model_picker import ModelPickerScreen
9
+ from shotgun.tui.screens.provider_config import ProviderConfigScreen
8
10
 
9
11
  if TYPE_CHECKING:
10
12
  from shotgun.tui.screens.chat import ChatScreen
@@ -139,7 +141,11 @@ class ProviderSetupProvider(Provider):
139
141
 
140
142
  def open_provider_config(self) -> None:
141
143
  """Show the provider configuration screen."""
142
- self.chat_screen.app.push_screen("provider_config")
144
+ self.chat_screen.app.push_screen(ProviderConfigScreen())
145
+
146
+ def open_model_picker(self) -> None:
147
+ """Show the model picker screen."""
148
+ self.chat_screen.app.push_screen(ModelPickerScreen())
143
149
 
144
150
  async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
145
151
  yield DiscoveryHit(
@@ -147,9 +153,15 @@ class ProviderSetupProvider(Provider):
147
153
  self.open_provider_config,
148
154
  help="⚙️ Manage API keys for available providers",
149
155
  )
156
+ yield DiscoveryHit(
157
+ "Select AI Model",
158
+ self.open_model_picker,
159
+ help="🤖 Choose which AI model to use",
160
+ )
150
161
 
151
162
  async def search(self, query: str) -> AsyncGenerator[Hit, None]:
152
163
  matcher = self.matcher(query)
164
+
153
165
  title = "Open Provider Setup"
154
166
  score = matcher.match(title)
155
167
  if score > 0:
@@ -160,6 +172,16 @@ class ProviderSetupProvider(Provider):
160
172
  help="⚙️ Manage API keys for available providers",
161
173
  )
162
174
 
175
+ title = "Select AI Model"
176
+ score = matcher.match(title)
177
+ if score > 0:
178
+ yield Hit(
179
+ score,
180
+ matcher.highlight(title),
181
+ self.open_model_picker,
182
+ help="🤖 Choose which AI model to use",
183
+ )
184
+
163
185
 
164
186
  class CodebaseCommandProvider(Provider):
165
187
  """Command palette entries for codebase management."""
@@ -171,30 +193,30 @@ class CodebaseCommandProvider(Provider):
171
193
  return cast(ChatScreen, self.screen)
172
194
 
173
195
  async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
174
- yield DiscoveryHit(
175
- "Codebase: Index Codebase",
176
- self.chat_screen.index_codebase_command,
177
- help="Index a repository into the codebase graph",
178
- )
179
196
  yield DiscoveryHit(
180
197
  "Codebase: Delete Codebase Index",
181
198
  self.chat_screen.delete_codebase_command,
182
199
  help="Delete an existing codebase index",
183
200
  )
201
+ yield DiscoveryHit(
202
+ "Codebase: Index Codebase",
203
+ self.chat_screen.index_codebase_command,
204
+ help="Index a repository into the codebase graph",
205
+ )
184
206
 
185
207
  async def search(self, query: str) -> AsyncGenerator[Hit, None]:
186
208
  matcher = self.matcher(query)
187
209
  commands = [
188
- (
189
- "Codebase: Index Codebase",
190
- self.chat_screen.index_codebase_command,
191
- "Index a repository into the codebase graph",
192
- ),
193
210
  (
194
211
  "Codebase: Delete Codebase Index",
195
212
  self.chat_screen.delete_codebase_command,
196
213
  "Delete an existing codebase index",
197
214
  ),
215
+ (
216
+ "Codebase: Index Codebase",
217
+ self.chat_screen.index_codebase_command,
218
+ "Index a repository into the codebase graph",
219
+ ),
198
220
  ]
199
221
  for title, callback, help_text in commands:
200
222
  score = matcher.match(title)
@@ -249,3 +271,88 @@ class DeleteCodebasePaletteProvider(Provider):
249
271
  ),
250
272
  help=graph.repo_path,
251
273
  )
274
+
275
+
276
+ class UnifiedCommandProvider(Provider):
277
+ """Unified command provider with all commands in alphabetical order."""
278
+
279
+ @property
280
+ def chat_screen(self) -> "ChatScreen":
281
+ from shotgun.tui.screens.chat import ChatScreen
282
+
283
+ return cast(ChatScreen, self.screen)
284
+
285
+ def open_provider_config(self) -> None:
286
+ """Show the provider configuration screen."""
287
+ self.chat_screen.app.push_screen(ProviderConfigScreen())
288
+
289
+ def open_model_picker(self) -> None:
290
+ """Show the model picker screen."""
291
+ self.chat_screen.app.push_screen(ModelPickerScreen())
292
+
293
+ async def discover(self) -> AsyncGenerator[DiscoveryHit, None]:
294
+ """Provide commands in alphabetical order when palette opens."""
295
+ # Alphabetically ordered commands
296
+ yield DiscoveryHit(
297
+ "Codebase: Delete Codebase Index",
298
+ self.chat_screen.delete_codebase_command,
299
+ help="Delete an existing codebase index",
300
+ )
301
+ yield DiscoveryHit(
302
+ "Codebase: Index Codebase",
303
+ self.chat_screen.index_codebase_command,
304
+ help="Index a repository into the codebase graph",
305
+ )
306
+ yield DiscoveryHit(
307
+ "Open Provider Setup",
308
+ self.open_provider_config,
309
+ help="⚙️ Manage API keys for available providers",
310
+ )
311
+ yield DiscoveryHit(
312
+ "Select AI Model",
313
+ self.open_model_picker,
314
+ help="🤖 Choose which AI model to use",
315
+ )
316
+ yield DiscoveryHit(
317
+ "Show usage",
318
+ self.chat_screen.action_show_usage,
319
+ help="Display usage information for the current session",
320
+ )
321
+
322
+ async def search(self, query: str) -> AsyncGenerator[Hit, None]:
323
+ """Search for commands in alphabetical order."""
324
+ matcher = self.matcher(query)
325
+
326
+ # Define all commands in alphabetical order
327
+ commands = [
328
+ (
329
+ "Codebase: Delete Codebase Index",
330
+ self.chat_screen.delete_codebase_command,
331
+ "Delete an existing codebase index",
332
+ ),
333
+ (
334
+ "Codebase: Index Codebase",
335
+ self.chat_screen.index_codebase_command,
336
+ "Index a repository into the codebase graph",
337
+ ),
338
+ (
339
+ "Open Provider Setup",
340
+ self.open_provider_config,
341
+ "⚙️ Manage API keys for available providers",
342
+ ),
343
+ (
344
+ "Select AI Model",
345
+ self.open_model_picker,
346
+ "🤖 Choose which AI model to use",
347
+ ),
348
+ (
349
+ "Show usage",
350
+ self.chat_screen.action_show_usage,
351
+ "Display usage information for the current session",
352
+ ),
353
+ ]
354
+
355
+ for title, callback, help_text in commands:
356
+ score = matcher.match(title)
357
+ if score > 0:
358
+ yield Hit(score, matcher.highlight(title), callback, help=help_text)
@@ -217,7 +217,9 @@ class AgentResponseWidget(Widget):
217
217
  return ""
218
218
  for idx, part in enumerate(self.item.parts):
219
219
  if isinstance(part, TextPart):
220
- acc += f"**⏺** {part.content}\n\n"
220
+ # Only show the circle prefix if there's actual content
221
+ if part.content and part.content.strip():
222
+ acc += f"**⏺** {part.content}\n\n"
221
223
  elif isinstance(part, ToolCallPart):
222
224
  parts_str = self._format_tool_call_part(part)
223
225
  acc += parts_str + "\n\n"
@@ -182,12 +182,12 @@ class FeedbackScreen(Screen[Feedback | None]):
182
182
  return
183
183
 
184
184
  app = cast("ShotgunApp", self.app)
185
- user_id = app.config_manager.get_user_id()
185
+ shotgun_instance_id = app.config_manager.get_shotgun_instance_id()
186
186
 
187
187
  feedback = Feedback(
188
188
  kind=self.selected_kind,
189
189
  description=description,
190
- user_id=user_id,
190
+ shotgun_instance_id=shotgun_instance_id,
191
191
  )
192
192
 
193
193
  self.dismiss(feedback)