mirror of
https://github.com/smkrv/ha-text-ai.git
synced 2026-07-31 01:33:57 +08:00
feat: v2.5.0 - disable_thinking, reasoning models, MIT license, security hardening
Features: - disable_thinking toggle (issue #11) with per-provider semantics: OpenAI classic gets /no_think soft-switch, OpenAI reasoning gets reasoning_effort=low, DeepSeek gets both, Anthropic no-op (thinking opt-in), Gemini 2.5+ gets thinking_budget=0 - OpenAI reasoning model support (o1/o3/o4-mini/gpt-5 family): max_completion_tokens, developer role, reasoning_effort - Per-request disable_thinking override in ask_question service - Provider-specific temperature clip (Anthropic 0-1, others 0-2) Security: - Require API key re-entry on provider change (OptionsFlow) - Validate Anthropic json_schema before system-prompt concatenation - Symlink protection in history file operations - Hardened secret redaction regexes (sk-*, AIza*, x-api-key) - get_history service: hard cap on limit (default 10, max 100) Reliability: - Retry on 502/503/504 in addition to 429/timeout - Honor Retry-After header on 429 - Exception chaining (raise ... from err) - _strip_think_blocks handles nested/dangling tags - normalize_name sha256 fallback for empty-collapse inputs UI/housekeeping: - api_key uses TextSelector(type=PASSWORD) - DeviceInfo entry_type=SERVICE - Services unregister on last entry unload - Dependabot for GitHub Actions - persist-credentials=false in checkout steps - manifest requirements pinned with upper bounds - Ignore docs/plans/ (working artifacts) License: PolyForm Noncommercial 1.0.0 -> MIT No breaking changes: service response shape, sensor attributes, entity IDs and on-disk formats unchanged.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""
|
||||
History management for HA Text AI integration.
|
||||
|
||||
@license: PolyForm Noncommercial 1.0.0 (https://polyformproject.org/licenses/noncommercial/1.0.0)
|
||||
@license: MIT (https://opensource.org/licenses/MIT)
|
||||
@author: SMKRV
|
||||
@github: https://github.com/smkrv/ha-text-ai
|
||||
@source: https://github.com/smkrv/ha-text-ai
|
||||
@@ -50,6 +50,18 @@ class AsyncFileHandler:
|
||||
await self.file.close()
|
||||
|
||||
|
||||
def _assert_not_symlink(path: str) -> None:
|
||||
"""Refuse to operate on a path that resolves to a symlink.
|
||||
|
||||
Why: another component or an attacker with filesystem access could
|
||||
replace our history file with a symlink pointing at arbitrary disk
|
||||
locations. Then os.remove or shutil.move would hit the target
|
||||
instead of our managed file. Check before destructive ops.
|
||||
"""
|
||||
if os.path.islink(path):
|
||||
raise OSError(f"Refusing to operate on symlink: {path}")
|
||||
|
||||
|
||||
class HistoryManager:
|
||||
"""Manages conversation history for an instance."""
|
||||
|
||||
@@ -242,6 +254,9 @@ class HistoryManager:
|
||||
f"{self.normalized_name}_history_{dt_util.utcnow().strftime('%Y%m%d_%H%M%S')}.json",
|
||||
)
|
||||
|
||||
await self.hass.async_add_executor_job(
|
||||
_assert_not_symlink, self._history_file
|
||||
)
|
||||
await self.hass.async_add_executor_job(
|
||||
shutil.move, self._history_file, archive_file
|
||||
)
|
||||
@@ -280,6 +295,9 @@ class HistoryManager:
|
||||
archives = await self.hass.async_add_executor_job(find_archives)
|
||||
if len(archives) > MAX_ARCHIVE_FILES:
|
||||
for old_file in archives[:-MAX_ARCHIVE_FILES]:
|
||||
await self.hass.async_add_executor_job(
|
||||
_assert_not_symlink, old_file
|
||||
)
|
||||
await self.hass.async_add_executor_job(os.remove, old_file)
|
||||
_LOGGER.debug("Removed old archive: %s", old_file)
|
||||
except Exception as e:
|
||||
@@ -359,6 +377,9 @@ class HistoryManager:
|
||||
try:
|
||||
self._conversation_history = []
|
||||
if await self._file_exists(self._history_file):
|
||||
await self.hass.async_add_executor_job(
|
||||
_assert_not_symlink, self._history_file
|
||||
)
|
||||
await self.hass.async_add_executor_job(os.remove, self._history_file)
|
||||
_LOGGER.info("History for %s cleared", self.instance_name)
|
||||
except Exception as e:
|
||||
|
||||
Reference in New Issue
Block a user