tumblrbot 1.3.1__py3-none-any.whl → 1.3.2__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.
@@ -4,7 +4,10 @@ from textwrap import dedent
4
4
  from time import sleep
5
5
 
6
6
  import rich
7
+ from openai import BadRequestError
8
+ from openai.types import FileObject
7
9
  from openai.types.fine_tuning import FineTuningJob
10
+ from tenacity import retry, retry_if_exception_type, stop_after_attempt, wait_fixed, wait_random
8
11
 
9
12
  from tumblrbot.utils.common import FlowClass, PreviewLive
10
13
 
@@ -46,6 +49,18 @@ class FineTuner(FlowClass):
46
49
 
47
50
  return job
48
51
 
52
+ @retry(
53
+ stop=stop_after_attempt(5),
54
+ wait=wait_fixed(1.5) + wait_random(),
55
+ retry=retry_if_exception_type(BadRequestError),
56
+ reraise=True,
57
+ )
58
+ def attempt_submit_job(self, file: FileObject) -> FineTuningJob:
59
+ return self.openai.fine_tuning.jobs.create(
60
+ model=self.config.base_model,
61
+ training_file=file.id,
62
+ )
63
+
49
64
  def create_job(self, live: PreviewLive) -> FineTuningJob:
50
65
  if self.config.job_id:
51
66
  return self.poll_job_status()
@@ -56,10 +71,7 @@ class FineTuner(FlowClass):
56
71
  purpose="fine-tune",
57
72
  )
58
73
 
59
- job = self.openai.fine_tuning.jobs.create(
60
- model=self.config.base_model,
61
- training_file=file.id,
62
- )
74
+ job = self.attempt_submit_job(file)
63
75
 
64
76
  self.config.job_id = job.id
65
77
  return job
@@ -84,6 +96,7 @@ class FineTuner(FlowClass):
84
96
 
85
97
  live.progress.update(
86
98
  task_id,
99
+ total=job.estimated_finish,
87
100
  description=f"Fine-tuning: [italic]{job.status.replace('_', ' ').title()}[/]...",
88
101
  )
89
102
 
tumblrbot/utils/config.py CHANGED
@@ -5,7 +5,7 @@ from typing import TYPE_CHECKING, Self, override
5
5
  import rich
6
6
  import tomlkit
7
7
  from openai.types import ChatModel
8
- from pydantic import Field, PositiveFloat, PositiveInt, Secret, model_validator
8
+ from pydantic import Field, NonNegativeFloat, PositiveFloat, PositiveInt, Secret, model_validator
9
9
  from pydantic_settings import BaseSettings, PydanticBaseSettingsSource, SettingsConfigDict, TomlConfigSettingsSource
10
10
  from rich.prompt import Prompt
11
11
  from tomlkit import comment, document
@@ -32,7 +32,7 @@ class Config(BaseSettings):
32
32
  description="The identifier of the blog which generated drafts will be uploaded to. This must be a blog associated with the same account as the configured Tumblr secret tokens.",
33
33
  )
34
34
  draft_count: PositiveInt = Field(150, description="The number of drafts to process. This will affect the number of tokens used with OpenAI")
35
- tags_chance: float = Field(0.1, description="The chance to generate tags for any given post. This will incur extra calls to OpenAI.")
35
+ tags_chance: NonNegativeFloat = Field(0.1, description="The chance to generate tags for any given post. This will incur extra calls to OpenAI.")
36
36
 
37
37
  download_blog_identifiers: list[str] = Field(
38
38
  [],
tumblrbot/utils/models.py CHANGED
@@ -109,15 +109,10 @@ class Post(FullyValidatedModel):
109
109
  )
110
110
 
111
111
  def only_text_blocks(self) -> bool:
112
- return all(block.type == "text" for block in self.content)
112
+ return all(block.type == "text" for block in self.content) and not any(block.type == "ask" for block in self.layout)
113
113
 
114
114
  def get_content_text(self) -> str:
115
- blocks = dict(enumerate(block.text for block in self.content))
116
- for block in self.layout:
117
- if block.type == "ask":
118
- for i in block.blocks:
119
- del blocks[i]
120
- return "\n\n".join(blocks.values())
115
+ return "\n\n".join(block.text for block in self.content)
121
116
 
122
117
 
123
118
  class Example(FullyValidatedModel):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: tumblrbot
3
- Version: 1.3.1
3
+ Version: 1.3.2
4
4
  Summary: An updated bot that posts to Tumblr, based on your very own blog!
5
5
  Requires-Python: >= 3.13
6
6
  Description-Content-Type: text/markdown
@@ -12,6 +12,7 @@ Requires-Dist: pydantic-settings
12
12
  Requires-Dist: requests
13
13
  Requires-Dist: requests-oauthlib
14
14
  Requires-Dist: rich
15
+ Requires-Dist: tenacity
15
16
  Requires-Dist: tiktoken
16
17
  Requires-Dist: tomlkit
17
18
  Project-URL: Source, https://github.com/MaidThatPrograms/tumblrbot
@@ -57,7 +58,6 @@ Features:
57
58
  - Filters out posts that contain more than just text data.
58
59
  - Filters out any posts flagged by the [OpenAI] [Moderation API] (optional).
59
60
  - Shows progress and previews the current post.
60
- - Formats asks as the user message and the responses as the assistant response.
61
61
  - Adds custom user messages and assistant responses to the dataset from the [configured][config] file.
62
62
  1. Provides cost estimates if the currently saved examples are used to fine-tune the [configured][config] model.
63
63
  1. [Uploads examples][Fine-Tune] to [OpenAI] and begins the fine-tuning process.
@@ -90,11 +90,14 @@ Features:
90
90
  Run `tumblrbot` from anywhere. Run `tumblrbot --help` for command-line options. Every command-line option corresponds to a value from the [config](#configuration).
91
91
 
92
92
  ## Obtaining Tokens
93
- - The [OpenAI] API token can be created [here](https://platform.openai.com/settings/organization/api-keys).
93
+ ### OpenAI
94
+ API token can be created [here](https://platform.openai.com/settings/organization/api-keys).
94
95
  1. Leave everything at the defaults and set `Project` to `Default Project`.
95
96
  1. Press `Create secret key`.
96
97
  1. Press `Copy` to copy the API token to your clipboard.
97
- - The [Tumblr] API tokens can be created [here](https://tumblr.com/oauth/apps).
98
+
99
+ ### Tumblr
100
+ API tokens can be created [here](https://tumblr.com/oauth/apps).
98
101
  1. Press `+ Register Application`.
99
102
  1. Enter anything for `Application Name` and `Application Description`.
100
103
  1. Enter any URL for `Application Website` and `Default callback URL`, like `https://example.com`.
@@ -3,14 +3,14 @@ tumblrbot/__main__.py,sha256=uth-d5QsXa-BilOHrgh7Dky4431RLIf9-SS44FFvyOA,1516
3
3
  tumblrbot/flow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  tumblrbot/flow/download.py,sha256=CzGv3gpVKFqB0mEbFGk0tJmtwRZM8isvJ7pOPqVfIY0,2307
5
5
  tumblrbot/flow/examples.py,sha256=QQWrPo8g6KRFmEaydnk91fBjI2a7ey3vlxsgecuF2yk,4947
6
- tumblrbot/flow/fine_tune.py,sha256=jPKMub46pM4MxczUf1oLwNqxs5vtjoNu4noW9E7RIY4,4016
6
+ tumblrbot/flow/fine_tune.py,sha256=q5J1fL5ICjL7B72ceHRRbArj345DA0mX0_f6DYX5P4Q,4538
7
7
  tumblrbot/flow/generate.py,sha256=PeMMfTLD41iLBbVnNhvdM2UzyOtoTK3OS0R4YLxZjMA,2214
8
8
  tumblrbot/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
9
  tumblrbot/utils/common.py,sha256=GjyTJmU3NEjxr6Bhta-RmKlvboH4Qhi8Si6Yw5N8BGo,1340
10
- tumblrbot/utils/config.py,sha256=LOApMYHAggHT2rIhrPY72zf6szT7bwB33oSm4lKtEy8,5001
11
- tumblrbot/utils/models.py,sha256=elYGUMw42knx4Shrp2bBkPEuy7V_wFBgEzdzpKlEu7U,5415
10
+ tumblrbot/utils/config.py,sha256=32AiFIYXzlNG1K-I_zjflfiEjHJ9uUhfm3OcYzNgv_I,5030
11
+ tumblrbot/utils/models.py,sha256=KmJIxTLZNcrd5sNwN2ldKVr8JGlmcSksH6v8e9zFWkg,5275
12
12
  tumblrbot/utils/tumblr.py,sha256=z4kdtHSRx89AQPlLpQ_Vcdfs3tZToFS89k6QKeBu42s,1243
13
- tumblrbot-1.3.1.dist-info/entry_points.txt,sha256=lTiN7PxAbyGY1fpCWApEw6NUIUgobfcOKhvn6cu3IQA,53
14
- tumblrbot-1.3.1.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
15
- tumblrbot-1.3.1.dist-info/METADATA,sha256=NCDCJAA7yRAcFC6slOViAGTxGq37WUh3yEQ5vPX2Lbg,6232
16
- tumblrbot-1.3.1.dist-info/RECORD,,
13
+ tumblrbot-1.3.2.dist-info/entry_points.txt,sha256=lTiN7PxAbyGY1fpCWApEw6NUIUgobfcOKhvn6cu3IQA,53
14
+ tumblrbot-1.3.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82
15
+ tumblrbot-1.3.2.dist-info/METADATA,sha256=KjZINv3bcH64q5SXIB9VJMEkQR4X0bifUf-2q6mgr6I,6163
16
+ tumblrbot-1.3.2.dist-info/RECORD,,