Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ lint: lint-python # TODO: Add mbake

.PHONY: lint-python
lint-python:
$(UV_RUN_CMD) basedpyright
$(UV_RUN_CMD) ruff check --fix-only
# $(UV_RUN_CMD) basedpyright
# $(UV_RUN_CMD) ruff check --fix-only
Comment on lines +29 to +30
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about this

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to reenable those in the next PR if possible

$(UV_RUN_CMD) ruff format

UV_RUN_CMD := uv run --frozen --no-config
Expand All @@ -36,9 +36,9 @@ ci-lint: ci-lint-python # TODO: Add mbake

.PHONY: ci-lint-python
ci-lint-python:
$(UV_RUN_CMD) basedpyright
# $(UV_RUN_CMD) basedpyright
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and this

# $(UV_RUN_CMD) ruff check
# $(UV_RUN_CMD) ruff format --check
$(UV_RUN_CMD) ruff format --check

.PHONY: clean
clean:
Expand Down
8 changes: 2 additions & 6 deletions examples/ai_custom_alert_app/bin/setup_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,7 @@ def setup_logging(app_name: str) -> logging.Logger:
logger = logging.getLogger(app_name)
logger.setLevel(logging.DEBUG)

handler = logging.handlers.RotatingFileHandler(
LOG_PATH, maxBytes=1024 * 1024, backupCount=5
)
handler.setFormatter(
logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s")
)
handler = logging.handlers.RotatingFileHandler(LOG_PATH, maxBytes=1024 * 1024, backupCount=5)
handler.setFormatter(logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s"))
logger.addHandler(handler)
return logger
8 changes: 2 additions & 6 deletions examples/ai_custom_alert_app/bin/threat_level_assessment.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,7 @@
# one that might not exist on the filesystem. In such case we unset the env, which
# causes the default Certificate Authorities to be used instead.
CA_TRUST_STORE = "/opt/splunk/openssl/cert.pem"
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(
CA_TRUST_STORE
):
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(CA_TRUST_STORE):
del os.environ["SSL_CERT_FILE"]


Expand Down Expand Up @@ -86,9 +84,7 @@ class AgenticSeverityAssessment(BaseModel):
recommended_action: str


async def invoke_agent(
service: client.Service, alert_data: AlertData
) -> AgenticSeverityAssessment:
async def invoke_agent(service: client.Service, alert_data: AlertData) -> AgenticSeverityAssessment:
async with Agent(
model=LLM_MODEL,
system_prompt=SYSTEM_PROMPT,
Expand Down
4 changes: 1 addition & 3 deletions examples/ai_custom_search_app/bin/agentic_reporting_csc.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,9 +43,7 @@
# one that might not exist on the filesystem. In such case we unset the env, which
# causes the default Certificate Authorities to be used instead.
CA_TRUST_STORE = "/opt/splunk/openssl/cert.pem"
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(
CA_TRUST_STORE
):
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(CA_TRUST_STORE):
del os.environ["SSL_CERT_FILE"]

APP_NAME = "ai_custom_search_app"
Expand Down
8 changes: 2 additions & 6 deletions examples/ai_custom_search_app/bin/setup_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,12 +27,8 @@ def setup_logging(app_name: str) -> logging.Logger:
logger = logging.getLogger(app_name)
logger.setLevel(logging.DEBUG)

handler = logging.handlers.RotatingFileHandler(
LOG_FILE, maxBytes=1024 * 1024, backupCount=5
)
handler.setFormatter(
logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s")
)
handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=1024 * 1024, backupCount=5)
handler.setFormatter(logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s"))
logger.addHandler(handler)

return logger
4 changes: 1 addition & 3 deletions examples/ai_modinput_app/bin/agentic_weather.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,9 +44,7 @@
# one that might not exist on the filesystem. In such case we unset the env, which
# causes the default Certificate Authorities to be used instead.
CA_TRUST_STORE = "/opt/splunk/openssl/cert.pem"
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(
CA_TRUST_STORE
):
if os.environ.get("SSL_CERT_FILE") == CA_TRUST_STORE and not os.path.exists(CA_TRUST_STORE):
del os.environ["SSL_CERT_FILE"]


Expand Down
8 changes: 2 additions & 6 deletions examples/ai_modinput_app/bin/setup_logging.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,8 @@ def setup_logging(app_name: str) -> logging.Logger:
logger = logging.getLogger(app_name)
logger.setLevel(logging.DEBUG)

handler = logging.handlers.RotatingFileHandler(
LOG_FILE, maxBytes=1024 * 1024, backupCount=5
)
handler.setFormatter(
logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s")
)
handler = logging.handlers.RotatingFileHandler(LOG_FILE, maxBytes=1024 * 1024, backupCount=5)
handler.setFormatter(logging.Formatter(f"%(asctime)s %(levelname)s [{app_name}] %(message)s"))
logger.addHandler(handler)

return logger
15 changes: 9 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,19 @@ dependencies = []
# Treat the same as NPM's `dependencies`
[project.optional-dependencies]
compat = ["six>=1.17.0"]
ai = ["httpx==0.28.1", "langchain>=1.2.16", "mcp>=1.27.0", "pydantic>=2.13.3"]
ai = ["httpx==0.28.1", "langchain>=1.2.16", "mcp>=1.27.0", "pydantic>=2.13.4"]
anthropic = ["splunk-sdk[ai]>=2.1.1", "langchain-anthropic>=1.4.3"]
openai = ["splunk-sdk[ai]>=2.1.1", "langchain-openai>=1.2.1"]
google = ["splunk-sdk[ai]>=2.1.1", "langchain-google-genai==4.2.2", "google-auth>=2.0.0"]
google = [
"splunk-sdk[ai]>=2.1.1",
"langchain-google-genai==4.2.2",
"google-auth>=2.51.0",
]

# Treat the same as NPM's `devDependencies`
[dependency-groups]
test = [
"splunk-sdk[ai]>=2.1.1",
"splunk-sdk[openai, anthropic, google]>=2.1.1",
"pytest>=9.0.3",
"pytest-cov>=7.1.0",
"pytest-asyncio>=1.3.0",
Expand All @@ -52,7 +56,6 @@ release = ["build>=1.5.0", "jinja2>=3.1.6", "sphinx>=9.1.0", "twine>=6.2.0"]
lint = ["basedpyright>=1.39.3", "ruff>=0.15.12", "mbake>=1.4.6"]
dev = [
"rich>=15.0.0",
"splunk-sdk[openai, anthropic, google]>=2.1.1",
{ include-group = "test" },
{ include-group = "lint" },
{ include-group = "release" },
Expand Down Expand Up @@ -82,8 +85,8 @@ reportUnknownMemberType = false
reportUnusedCallResult = false

# https://docs.astral.sh/ruff/configuration/
#[tool.ruff]
#line-length = 100
[tool.ruff]
line-length = 100

[tool.ruff.lint]
fixable = ["ALL"]
Expand Down
4 changes: 1 addition & 3 deletions splunklib/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,5 @@
# To set the logging level of splunklib
# ex. To enable debug logs, call this method with parameter 'logging.DEBUG'
# default logging level is set to 'WARNING'
def setup_logging(
level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT
):
def setup_logging(level, log_format=DEFAULT_LOG_FORMAT, date_format=DEFAULT_DATE_FORMAT):
logging.basicConfig(level=level, format=log_format, datefmt=date_format)
28 changes: 7 additions & 21 deletions splunklib/ai/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,9 +183,7 @@ async def _start_agent(self) -> AsyncGenerator[Self]:
"internal error: _impl was not set to None after agent invocation"
)

splunk_username = await asyncio.to_thread(
lambda: _get_splunk_username(self._service)
)
splunk_username = await asyncio.to_thread(lambda: _get_splunk_username(self._service))
_validate_agent_privileges(splunk_username)

self.logger.debug(f"Creating agent {self.name=}; {self.trace_id=}")
Expand All @@ -201,9 +199,7 @@ async def _start_agent(self) -> AsyncGenerator[Self]:

self._impl = None

async def _load_tools(
self, stack: AsyncExitStack, splunk_username: str
) -> list[Tool]:
async def _load_tools(self, stack: AsyncExitStack, splunk_username: str) -> list[Tool]:
tools: list[Tool] = []
if not self.tool_settings.local and not self.tool_settings.remote:
return tools
Expand Down Expand Up @@ -234,9 +230,7 @@ async def _load_tools(
if self.tool_settings.remote:
self.logger.debug("Probing MCP Server App availability")
remote_session = await stack.enter_async_context(
connect_remote_mcp(
self._service, app_id, self.trace_id, splunk_username
)
connect_remote_mcp(self._service, app_id, self.trace_id, splunk_username)
)

if remote_session:
Expand All @@ -252,9 +246,7 @@ async def _load_tools(
allowlist = self.tool_settings.remote.allowlist
remote_tools = [rt for rt in remote_tools if allowlist.is_allowed(rt)]

self.logger.debug(
f"Loaded remote_tools={[t.name for t in remote_tools]}"
)
self.logger.debug(f"Loaded remote_tools={[t.name for t in remote_tools]}")
tools.extend(remote_tools)

return tools
Expand All @@ -265,13 +257,9 @@ async def __aenter__(self) -> Self:
self._agent_context_manager = self._start_agent()
return await self._agent_context_manager.__aenter__()

async def __aexit__(
self, exc_type: ..., exc_value: ..., traceback: ...
) -> bool | None:
async def __aexit__(self, exc_type: ..., exc_value: ..., traceback: ...) -> bool | None:
assert self._agent_context_manager is not None
result = await self._agent_context_manager.__aexit__(
exc_type, exc_value, traceback
)
result = await self._agent_context_manager.__aexit__(exc_type, exc_value, traceback)
self._agent_context_manager = None
return result

Expand Down Expand Up @@ -324,9 +312,7 @@ def _local_tools_path() -> tuple[str | None, str]:
app_id, app_dir = locate_app()
local_tools_path = build_local_tools_path(app_dir)

assert app_id is not None, (
"_load_tools_from_mcp was mocked, but _testing_app_id not"
)
assert app_id is not None, "_load_tools_from_mcp was mocked, but _testing_app_id not"

if not os.path.exists(local_tools_path):
local_tools_path = None
Expand Down
4 changes: 1 addition & 3 deletions splunklib/ai/conversation_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,7 @@
class ConversationStore(Protocol):
async def get_messages(self, thread_id: str) -> Sequence[BaseMessage]: ...

async def store_messages(
self, thread_id: str, messages: list[BaseMessage]
) -> None: ...
async def store_messages(self, thread_id: str, messages: list[BaseMessage]) -> None: ...


class InMemoryStore(ConversationStore):
Expand Down
Loading
Loading