goose-py 0.7.0__tar.gz → 0.7.2__tar.gz

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.
Files changed (36) hide show
  1. goose_py-0.7.2/.github/workflows/publish.yml +48 -0
  2. goose_py-0.7.2/Makefile +9 -0
  3. {goose_py-0.7.0 → goose_py-0.7.2}/PKG-INFO +1 -1
  4. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/agent.py +7 -25
  5. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/conversation.py +10 -16
  6. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/flow.py +3 -4
  7. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/state.py +9 -15
  8. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/task.py +11 -12
  9. {goose_py-0.7.0 → goose_py-0.7.2}/pyproject.toml +3 -2
  10. {goose_py-0.7.0 → goose_py-0.7.2}/tests/conftest.py +1 -0
  11. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_agent.py +5 -11
  12. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_complex_flow_arguments.py +1 -2
  13. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_downstream_task.py +1 -3
  14. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_jamming.py +2 -4
  15. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_looping.py +2 -6
  16. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_state.py +31 -2
  17. {goose_py-0.7.0 → goose_py-0.7.2}/uv.lock +2 -2
  18. goose_py-0.7.0/.github/workflows/publish.yml +0 -75
  19. {goose_py-0.7.0 → goose_py-0.7.2}/.envrc +0 -0
  20. {goose_py-0.7.0 → goose_py-0.7.2}/.gitignore +0 -0
  21. {goose_py-0.7.0 → goose_py-0.7.2}/.python-version +0 -0
  22. {goose_py-0.7.0 → goose_py-0.7.2}/.stubs/jsonpath_ng/__init__.pyi +0 -0
  23. {goose_py-0.7.0 → goose_py-0.7.2}/.stubs/litellm/__init__.pyi +0 -0
  24. {goose_py-0.7.0 → goose_py-0.7.2}/README.md +0 -0
  25. {goose_py-0.7.0 → goose_py-0.7.2}/goose/__init__.py +0 -0
  26. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/result.py +0 -0
  27. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/store.py +0 -0
  28. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/types/__init__.py +0 -0
  29. {goose_py-0.7.0 → goose_py-0.7.2}/goose/_internal/types/agent.py +0 -0
  30. {goose_py-0.7.0 → goose_py-0.7.2}/goose/agent.py +0 -0
  31. {goose_py-0.7.0 → goose_py-0.7.2}/goose/errors.py +0 -0
  32. {goose_py-0.7.0 → goose_py-0.7.2}/goose/flow.py +0 -0
  33. {goose_py-0.7.0 → goose_py-0.7.2}/goose/py.typed +0 -0
  34. {goose_py-0.7.0 → goose_py-0.7.2}/goose/runs.py +0 -0
  35. {goose_py-0.7.0 → goose_py-0.7.2}/tests/__init__.py +0 -0
  36. {goose_py-0.7.0 → goose_py-0.7.2}/tests/test_regenerate.py +0 -0
@@ -0,0 +1,48 @@
1
+ name: CI/CD
2
+
3
+ on:
4
+ push:
5
+ branches:
6
+ - main
7
+ tags:
8
+ - "v*"
9
+ pull_request:
10
+ branches:
11
+ - main
12
+
13
+ jobs:
14
+ publish:
15
+ runs-on: ubuntu-latest
16
+
17
+ permissions:
18
+ id-token: write
19
+
20
+ steps:
21
+ - uses: actions/checkout@v4
22
+ with:
23
+ fetch-depth: 0
24
+
25
+ - name: Setup Python
26
+ uses: actions/setup-python@v5
27
+ with:
28
+ python-version-file: .python-version
29
+
30
+ - name: Setup UV
31
+ uses: astral-sh/setup-uv@v5
32
+ with:
33
+ version: "0.5.25"
34
+
35
+ - name: Initialize environment
36
+ run: uv sync --all-extras --dev
37
+
38
+ - name: Run tests
39
+ run: uv run pytest
40
+ env:
41
+ GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
42
+
43
+ - name: Build package
44
+ run: uv build
45
+
46
+ - name: Publish package
47
+ run: uv publish
48
+ continue-on-error: true
@@ -0,0 +1,9 @@
1
+ test:
2
+ uv run pytest tests
3
+ lint:
4
+ uv run ruff check .
5
+ uv run ruff format .
6
+ uv run pyright .
7
+ publish:
8
+ uv build
9
+ uv publish
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: goose-py
3
- Version: 0.7.0
3
+ Version: 0.7.2
4
4
  Summary: A tool for AI workflows based on human-computer collaboration and structured output.
5
5
  Author-email: Nash Taylor <nash@chelle.ai>, Joshua Cook <joshua@chelle.ai>, Michael Sankur <michael@chelle.ai>
6
6
  Requires-Python: >=3.12
@@ -65,20 +65,12 @@ class AgentResponse[R: BaseModel | str](BaseModel):
65
65
  @computed_field
66
66
  @property
67
67
  def input_cost(self) -> float:
68
- return (
69
- self.INPUT_CENTS_PER_MILLION_TOKENS[self.model]
70
- * self.input_tokens
71
- / 1_000_000
72
- )
68
+ return self.INPUT_CENTS_PER_MILLION_TOKENS[self.model] * self.input_tokens / 1_000_000
73
69
 
74
70
  @computed_field
75
71
  @property
76
72
  def output_cost(self) -> float:
77
- return (
78
- self.OUTPUT_CENTS_PER_MILLION_TOKENS[self.model]
79
- * self.output_tokens
80
- / 1_000_000
81
- )
73
+ return self.OUTPUT_CENTS_PER_MILLION_TOKENS[self.model] * self.output_tokens / 1_000_000
82
74
 
83
75
  @computed_field
84
76
  @property
@@ -100,15 +92,9 @@ class AgentResponse[R: BaseModel | str](BaseModel):
100
92
  for part in message["content"]:
101
93
  if part["type"] == "image_url":
102
94
  part["image_url"] = "__MEDIA__"
103
- minimized_input_messages = [
104
- json.dumps(message) for message in minimized_input_messages
105
- ]
106
-
107
- output_message = (
108
- self.response.model_dump_json()
109
- if isinstance(self.response, BaseModel)
110
- else self.response
111
- )
95
+ minimized_input_messages = [json.dumps(message) for message in minimized_input_messages]
96
+
97
+ output_message = self.response.model_dump_json() if isinstance(self.response, BaseModel) else self.response
112
98
 
113
99
  return {
114
100
  "run_id": self.run_id,
@@ -161,9 +147,7 @@ class Agent:
161
147
 
162
148
  if response_model is TextResult:
163
149
  response = await acompletion(model=model.value, messages=rendered_messages)
164
- parsed_response = response_model.model_validate(
165
- {"text": response.choices[0].message.content}
166
- )
150
+ parsed_response = response_model.model_validate({"text": response.choices[0].message.content})
167
151
  else:
168
152
  response = await acompletion(
169
153
  model=model.value,
@@ -174,9 +158,7 @@ class Agent:
174
158
  "enforce_validation": True,
175
159
  },
176
160
  )
177
- parsed_response = response_model.model_validate_json(
178
- response.choices[0].message.content
179
- )
161
+ parsed_response = response_model.model_validate_json(response.choices[0].message.content)
180
162
 
181
163
  end_time = datetime.now()
182
164
  agent_response = AgentResponse(
@@ -1,12 +1,9 @@
1
+ from typing import Self
2
+
1
3
  from pydantic import BaseModel
2
4
 
3
5
  from goose._internal.result import Result
4
- from goose._internal.types.agent import (
5
- AssistantMessage,
6
- LLMMessage,
7
- SystemMessage,
8
- UserMessage,
9
- )
6
+ from goose._internal.types.agent import AssistantMessage, LLMMessage, SystemMessage, UserMessage
10
7
 
11
8
 
12
9
  class Conversation[R: Result](BaseModel):
@@ -24,18 +21,15 @@ class Conversation[R: Result](BaseModel):
24
21
  messages.append(self.context.render())
25
22
 
26
23
  for message_index in range(len(self.user_messages)):
27
- messages.append(
28
- AssistantMessage(
29
- text=self.result_messages[message_index].model_dump_json()
30
- ).render()
31
- )
24
+ messages.append(AssistantMessage(text=self.result_messages[message_index].model_dump_json()).render())
32
25
  messages.append(self.user_messages[message_index].render())
33
26
 
34
27
  if len(self.result_messages) > len(self.user_messages):
35
- messages.append(
36
- AssistantMessage(
37
- text=self.result_messages[-1].model_dump_json()
38
- ).render()
39
- )
28
+ messages.append(AssistantMessage(text=self.result_messages[-1].model_dump_json()).render())
40
29
 
41
30
  return messages
31
+
32
+ def undo(self) -> Self:
33
+ self.user_messages.pop()
34
+ self.result_messages.pop()
35
+ return self
@@ -1,6 +1,7 @@
1
+ from collections.abc import AsyncIterator, Awaitable, Callable
1
2
  from contextlib import asynccontextmanager
2
3
  from types import CodeType
3
- from typing import AsyncIterator, Awaitable, Callable, Protocol, overload
4
+ from typing import Protocol, overload
4
5
 
5
6
  from goose._internal.agent import Agent, IAgentLogger
6
7
  from goose._internal.conversation import Conversation
@@ -13,9 +14,7 @@ from goose.errors import Honk
13
14
  class IAdapter[ResultT: Result](Protocol):
14
15
  __code__: CodeType
15
16
 
16
- async def __call__(
17
- self, *, conversation: Conversation[ResultT], agent: Agent
18
- ) -> ResultT: ...
17
+ async def __call__(self, *, conversation: Conversation[ResultT], agent: Agent) -> ResultT: ...
19
18
 
20
19
 
21
20
  class Flow[**P]:
@@ -61,6 +61,10 @@ class NodeState[ResultT: Result](BaseModel):
61
61
  self.conversation.user_messages.append(message)
62
62
  return self
63
63
 
64
+ def undo(self) -> Self:
65
+ self.conversation.undo()
66
+ return self
67
+
64
68
 
65
69
  class FlowRun:
66
70
  def __init__(self) -> None:
@@ -97,23 +101,17 @@ class FlowRun:
97
101
  matching_nodes: list[NodeState[R]] = []
98
102
  for key, node_state in self._node_states.items():
99
103
  if key[0] == task.name:
100
- matching_nodes.append(
101
- NodeState[task.result_type].model_validate_json(node_state)
102
- )
104
+ matching_nodes.append(NodeState[task.result_type].model_validate_json(node_state))
103
105
  return sorted(matching_nodes, key=lambda node: node.index)
104
106
 
105
107
  def get[R: Result](self, *, task: "Task[Any, R]", index: int = 0) -> NodeState[R]:
106
- if (
107
- existing_node_state := self._node_states.get((task.name, index))
108
- ) is not None:
108
+ if (existing_node_state := self._node_states.get((task.name, index))) is not None:
109
109
  return NodeState[task.result_type].model_validate_json(existing_node_state)
110
110
  else:
111
111
  return NodeState[task.result_type](
112
112
  task_name=task.name,
113
113
  index=index,
114
- conversation=Conversation[task.result_type](
115
- user_messages=[], result_messages=[]
116
- ),
114
+ conversation=Conversation[task.result_type](user_messages=[], result_messages=[]),
117
115
  last_hash=0,
118
116
  )
119
117
 
@@ -143,9 +141,7 @@ class FlowRun:
143
141
  self._last_requested_indices = {}
144
142
  self._flow_name = flow_name
145
143
  self._id = run_id
146
- self._agent = Agent(
147
- flow_name=self.flow_name, run_id=self.id, logger=agent_logger
148
- )
144
+ self._agent = Agent(flow_name=self.flow_name, run_id=self.id, logger=agent_logger)
149
145
 
150
146
  def end(self) -> None:
151
147
  self._last_requested_indices = {}
@@ -177,9 +173,7 @@ class FlowRun:
177
173
  return flow_run
178
174
 
179
175
 
180
- _current_flow_run: ContextVar[FlowRun | None] = ContextVar(
181
- "current_flow_run", default=None
182
- )
176
+ _current_flow_run: ContextVar[FlowRun | None] = ContextVar("current_flow_run", default=None)
183
177
 
184
178
 
185
179
  def get_current_flow_run() -> FlowRun | None:
@@ -1,4 +1,5 @@
1
- from typing import Awaitable, Callable, overload
1
+ from collections.abc import Awaitable, Callable
2
+ from typing import overload
2
3
 
3
4
  from goose._internal.agent import Agent, GeminiModel, SystemMessage, UserMessage
4
5
  from goose._internal.conversation import Conversation
@@ -33,9 +34,7 @@ class Task[**P, R: Result]:
33
34
  def name(self) -> str:
34
35
  return self._generator.__name__
35
36
 
36
- async def generate(
37
- self, state: NodeState[R], *args: P.args, **kwargs: P.kwargs
38
- ) -> R:
37
+ async def generate(self, state: NodeState[R], *args: P.args, **kwargs: P.kwargs) -> R:
39
38
  state_hash = self.__hash_task_call(*args, **kwargs)
40
39
  if state_hash != state.last_hash:
41
40
  result = await self._generator(*args, **kwargs)
@@ -58,14 +57,18 @@ class Task[**P, R: Result]:
58
57
  node_state.set_context(context=context)
59
58
  node_state.add_user_message(message=user_message)
60
59
 
61
- result = await self.__adapt(
62
- conversation=node_state.conversation, agent=flow_run.agent
63
- )
60
+ result = await self.__adapt(conversation=node_state.conversation, agent=flow_run.agent)
64
61
  node_state.add_result(result=result)
65
62
  flow_run.add_node_state(node_state)
66
63
 
67
64
  return result
68
65
 
66
+ def undo(self, *, index: int = 0) -> None:
67
+ flow_run = self.__get_current_flow_run()
68
+ node_state = flow_run.get(task=self, index=index)
69
+ node_state.undo()
70
+ flow_run.add_node_state(node_state)
71
+
69
72
  async def __call__(self, *args: P.args, **kwargs: P.kwargs) -> R:
70
73
  flow_run = self.__get_current_flow_run()
71
74
  node_state = flow_run.get_next(task=self)
@@ -97,11 +100,7 @@ class Task[**P, R: Result]:
97
100
 
98
101
  def __hash_task_call(self, *args: P.args, **kwargs: P.kwargs) -> int:
99
102
  try:
100
- to_hash = str(
101
- tuple(args)
102
- + tuple(kwargs.values())
103
- + (self._generator.__code__, self._adapter_model)
104
- )
103
+ to_hash = str(tuple(args) + tuple(kwargs.values()) + (self._generator.__code__, self._adapter_model))
105
104
  return hash(to_hash)
106
105
  except TypeError:
107
106
  raise Honk(f"Unhashable argument to task {self.name}: {args} {kwargs}")
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "goose-py"
3
- version = "0.7.0"
3
+ version = "0.7.2"
4
4
  description = "A tool for AI workflows based on human-computer collaboration and structured output."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -38,6 +38,7 @@ exclude = [
38
38
  ".venv",
39
39
  "**/.venv",
40
40
  "notebooks",
41
+ ".stubs",
41
42
  ]
42
43
  force-exclude = true
43
44
  line-length = 120
@@ -61,12 +62,12 @@ pythonVersion = "3.12"
61
62
  typeCheckingMode = "strict"
62
63
  reportMissingModuleSource = false
63
64
  useLibraryCodeForTypes = false
64
- reportImportCycles = true
65
65
  reportUnknownMemberType = false
66
66
  reportUnknownVariableType = false
67
67
  stubPath = ".stubs"
68
68
  venvPath = "."
69
69
  venv = ".venv"
70
+ exclude = ["goose/__init__.py", ".venv", "notebooks"]
70
71
 
71
72
  [tool.pytest.ini_options]
72
73
  filterwarnings = [
@@ -1,4 +1,5 @@
1
1
  import asyncio
2
+
2
3
  import pytest
3
4
 
4
5
 
@@ -5,26 +5,20 @@ from pytest_mock import MockerFixture
5
5
 
6
6
  from goose import TextResult, flow, task
7
7
  from goose._internal.agent import Agent, AgentResponse, IAgentLogger
8
- from goose.types.agent import GeminiModel, TextMessagePart, UserMessage
8
+ from goose.agent import GeminiModel, TextMessagePart, UserMessage
9
9
 
10
10
 
11
11
  class MockLiteLLMResponse:
12
- def __init__(
13
- self, *, response: str, prompt_tokens: int, completion_tokens: int
14
- ) -> None:
12
+ def __init__(self, *, response: str, prompt_tokens: int, completion_tokens: int) -> None:
15
13
  self.choices = [Mock(message=Mock(content=response))]
16
- self.usage = Mock(
17
- prompt_tokens=prompt_tokens, completion_tokens=completion_tokens
18
- )
14
+ self.usage = Mock(prompt_tokens=prompt_tokens, completion_tokens=completion_tokens)
19
15
 
20
16
 
21
17
  @pytest.fixture
22
18
  def mock_litellm(mocker: MockerFixture) -> Mock:
23
19
  return mocker.patch(
24
- "goose._agent.acompletion",
25
- return_value=MockLiteLLMResponse(
26
- response="Hello", prompt_tokens=10, completion_tokens=10
27
- ),
20
+ "goose._internal.agent.acompletion",
21
+ return_value=MockLiteLLMResponse(response="Hello", prompt_tokens=10, completion_tokens=10),
28
22
  )
29
23
 
30
24
 
@@ -1,8 +1,7 @@
1
1
  import pytest
2
2
  from pydantic import BaseModel
3
3
 
4
- from goose import flow
5
- from goose._agent import Agent
4
+ from goose import Agent, flow
6
5
 
7
6
 
8
7
  class MyMessage(BaseModel):
@@ -12,9 +12,7 @@ class GeneratedWord(Result):
12
12
 
13
13
  @task
14
14
  async def generate_random_word(*, n_characters: int) -> GeneratedWord:
15
- return GeneratedWord(
16
- word="".join(random.sample(string.ascii_lowercase, n_characters))
17
- )
15
+ return GeneratedWord(word="".join(random.sample(string.ascii_lowercase, n_characters)))
18
16
 
19
17
 
20
18
  @task
@@ -6,7 +6,7 @@ import pytest
6
6
  from pytest_mock import MockerFixture
7
7
 
8
8
  from goose import Result, flow, task
9
- from goose.types.agent import SystemMessage, TextMessagePart, UserMessage
9
+ from goose.agent import SystemMessage, TextMessagePart, UserMessage
10
10
 
11
11
 
12
12
  class GeneratedWord(Result):
@@ -19,9 +19,7 @@ class GeneratedSentence(Result):
19
19
 
20
20
  @task
21
21
  async def generate_random_word(*, n_characters: int) -> GeneratedWord:
22
- return GeneratedWord(
23
- word="".join(random.sample(string.ascii_lowercase, n_characters))
24
- )
22
+ return GeneratedWord(word="".join(random.sample(string.ascii_lowercase, n_characters)))
25
23
 
26
24
 
27
25
  @pytest.fixture
@@ -31,9 +31,7 @@ async def number_of_learning_outcomes() -> NumberOfLearningOutcomes:
31
31
 
32
32
 
33
33
  @task
34
- async def learning_outcome(
35
- *, objective: CourseObjective, previous_outcomes: list[LearningOutcome]
36
- ) -> LearningOutcome:
34
+ async def learning_outcome(*, objective: CourseObjective, previous_outcomes: list[LearningOutcome]) -> LearningOutcome:
37
35
  return LearningOutcome(outcome="Learn Python")
38
36
 
39
37
 
@@ -48,9 +46,7 @@ async def course_content() -> None:
48
46
  num_outcomes = await number_of_learning_outcomes()
49
47
  outcomes: list[LearningOutcome] = []
50
48
  for _ in range(num_outcomes.number):
51
- outcomes.append(
52
- await learning_outcome(objective=objective, previous_outcomes=outcomes)
53
- )
49
+ outcomes.append(await learning_outcome(objective=objective, previous_outcomes=outcomes))
54
50
 
55
51
  for outcome in outcomes:
56
52
  await quiz_question(outcome=outcome.outcome)
@@ -1,9 +1,12 @@
1
1
  import random
2
2
  import string
3
+ from unittest.mock import Mock
3
4
 
4
5
  import pytest
6
+ from pytest_mock import MockerFixture
5
7
 
6
8
  from goose import Result, flow, task
9
+ from goose._internal.types.agent import SystemMessage, TextMessagePart, UserMessage
7
10
  from goose.errors import Honk
8
11
 
9
12
 
@@ -17,8 +20,15 @@ class GeneratedSentence(Result):
17
20
 
18
21
  @task
19
22
  async def generate_random_word(*, n_characters: int) -> GeneratedWord:
20
- return GeneratedWord(
21
- word="".join(random.sample(string.ascii_lowercase, n_characters))
23
+ return GeneratedWord(word="".join(random.sample(string.ascii_lowercase, n_characters)))
24
+
25
+
26
+ @pytest.fixture
27
+ def generate_random_word_adapter(mocker: MockerFixture) -> Mock:
28
+ return mocker.patch.object(
29
+ generate_random_word,
30
+ "_Task__adapt",
31
+ return_value=GeneratedWord(word="__ADAPTED__"),
22
32
  )
23
33
 
24
34
 
@@ -49,3 +59,22 @@ async def test_state_causes_caching() -> None:
49
59
  new_random_word = new_run.get(task=generate_random_word).result.word
50
60
 
51
61
  assert random_word == new_random_word # unchanged node is not re-generated
62
+
63
+
64
+ @pytest.mark.asyncio
65
+ @pytest.mark.usefixtures("generate_random_word_adapter")
66
+ async def test_state_undo() -> None:
67
+ async with with_state.start_run(run_id="2"):
68
+ await with_state.generate()
69
+
70
+ async with with_state.start_run(run_id="2"):
71
+ await generate_random_word.jam(
72
+ index=0,
73
+ user_message=UserMessage(parts=[TextMessagePart(text="Change it")]),
74
+ context=SystemMessage(parts=[TextMessagePart(text="Extra info")]),
75
+ )
76
+
77
+ async with with_state.start_run(run_id="2") as run:
78
+ generate_random_word.undo()
79
+
80
+ assert run.get(task=generate_random_word).result.word != "__ADAPTED__"
@@ -233,8 +233,8 @@ wheels = [
233
233
  ]
234
234
 
235
235
  [[package]]
236
- name = "goose"
237
- version = "0.6.1"
236
+ name = "goose-py"
237
+ version = "0.7.2"
238
238
  source = { editable = "." }
239
239
  dependencies = [
240
240
  { name = "jsonpath-ng" },
@@ -1,75 +0,0 @@
1
- name: CI/CD
2
-
3
- on:
4
- push:
5
- branches:
6
- - main
7
- tags:
8
- - "v*"
9
- pull_request:
10
- branches:
11
- - main
12
-
13
- jobs:
14
- test:
15
- runs-on: ubuntu-latest
16
-
17
- steps:
18
- - uses: actions/checkout@v4
19
- with:
20
- fetch-depth: 0
21
-
22
- - name: Set up Python
23
- uses: actions/setup-python@v4
24
- with:
25
- python-version: "3.12"
26
-
27
- - name: Install Poetry
28
- uses: snok/install-poetry@v1
29
- with:
30
- version: 1.8.5
31
- virtualenvs-create: true
32
- virtualenvs-in-project: true
33
-
34
- - name: Install dependencies
35
- run: poetry install --all-extras
36
-
37
- - name: Run tests
38
- run: poetry run pytest tests
39
- env:
40
- GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
41
-
42
- publish:
43
- needs: test
44
- runs-on: ubuntu-latest
45
- if: github.event_name == 'push' && (startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/main')
46
- permissions:
47
- id-token: write
48
- contents: read
49
-
50
- steps:
51
- - uses: actions/checkout@v4
52
- with:
53
- fetch-depth: 0
54
-
55
- - name: Set up Python
56
- uses: actions/setup-python@v4
57
- with:
58
- python-version: "3.12"
59
-
60
- - name: Install Poetry
61
- uses: snok/install-poetry@v1
62
- with:
63
- version: 1.8.5
64
- virtualenvs-create: true
65
- virtualenvs-in-project: true
66
-
67
- - name: Install dependencies
68
- run: poetry install --all-extras
69
-
70
- - name: Build package
71
- run: poetry build
72
-
73
- - name: Publish to PyPI
74
- if: startsWith(github.ref, 'refs/tags/')
75
- uses: pypa/gh-action-pypi-publish@release/v1
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes