config.yaml

Every setting in a project's config.yaml.

A project's config.yaml tells Optics which driver to use, how to find elements, and where to write logs and results. Every setting lives in the project; there's no global config file and no environment variable that overrides it.

Run optics configure to write a working file by answering a few questions, then edit it by hand with this page.

How Optics finds the file#

CommandWhich file it reads
optics execute, optics dry_runAny YAML file in the project tree that has driver_sources and elements_sources. The file name doesn't matter. If there are several, the last one found wins.
optics liveThe first YAML file in the project tree with any config key.
optics doctor, optics configure, optics generateExactly <folder>/config.yaml.
optics serve, optics mcpNone. Settings come from each session request.

We recommend one file named config.yaml at the project root, so every command reads the same settings.

Settings you leave out take their defaults. Optics ignores keys it doesn't know, so a typo such as log_levle is silently skipped.

Top-level keys#

KeyDefaultWhat it does
driver_sourcesAll disabledThe driver that controls the device or browser. See driver_sources.
elements_sourcesAll disabledWhere Optics looks for elements: the driver's UI tree, its locators, or its screenshots. element_sources is accepted as an alias.
text_detectionAll disabledOCR engines for finding elements by on-screen text.
image_detectionAll disabledEngines for finding elements by image templates.
llm_modelsAll disabledThe LLM for AI self-heal and the plain-English mode of optics live.
log_levelINFODEBUG, INFO, WARNING, ERROR or CRITICAL, any case. Unknown values become INFO.
file_logfalseWrites internal_logs.log and execution_logs.log to the output folder.
log_pathNoneOne file for both logs instead of two. Used only with file_log: true.
json_logfalseWrites a JSON array of every test, module and keyword event to logs.json at the end of the run.
json_pathNoneWhere to write the JSON events instead of <output>/logs.json. ~ is expanded.
consoletrueHas no effect in 1.10.5. Nothing reads it. Use log_level to control console output.
execution_output_path<project>/execution_outputWhere results, screenshots and logs go. Relative paths are relative to the folder you run optics from.
includeNoneRun only these test cases. See include and exclude.
excludeNoneSkip these test cases.
ai_self_healfalseLets an LLM try to recover when an element can't be found. See ai_self_heal.
strict_element_matchfalseTurns off fuzzy and partial text matching. See strict_element_match.
save_capturestrueWhether Capture Screenshot, Capture Pagesource and Get Screen Elements write files. See save_captures.
halt_duration0.1Seconds to wait after a keyword fails every fallback. It isn't a pause between steps.
event_attributes_jsonNonePath to a JSON file of event attributes. When it includes an eventUrl, events you label with event_name are sent there at the end of the session.
project_pathThe project folderThe CLI always sets this to the folder you pass, so leave it out. It only matters for the Python SDK.

Engine lists#

driver_sources, elements_sources, text_detection, image_detection and llm_models are lists. Each item is an engine name with its settings:

driver_sources:
  - appium:                 # engine name
      enabled: true         # required: only enabled entries are loaded
      url: "http://127.0.0.1:4723"
      capabilities:         # engine-specific settings
        platformName: Android
FieldDefaultWhat it does
enabledRequiredOnly enabled entries are loaded. A disabled entry with any name is ignored.
urlNoneThe server address, for engines that use one.
capabilities{}Settings passed to the engine.

When several entries in one list are enabled, Optics tries them in the order you list them. For element sources this is the order in which strategies run.

An enabled entry with an unknown name fails with E0601. So does an enabled engine whose extra isn't installed. A list you write replaces the default list completely.

driver_sources#

Enable one driver. optics live refuses more than one, and optics doctor warns about it.

NameExtraWhat it drives
appiumappiumAndroid, iOS, Android TV, Samsung Tizen TV and LG webOS TV apps through an Appium server.
seleniumseleniumChrome or Firefox through a Selenium server or Grid.
playwrightplaywrightA local Chromium browser. No server needed.
blebleAny device paired with a Bluetooth mouse-and-keyboard dongle. For devices with no automation access.

elements_sources#

Enable the sources that match your driver. We recommend all three for your driver: locators, page source and screenshots.

NameNeeds driverWhat it gives you
appium_find_elementappiumFinds elements by XPath, accessibility id and class.
appium_page_sourceappiumSearches the UI tree by XPath and text, with fuzzy matching. Lists interactive elements.
appium_screenshotappiumScreenshots for OCR, image matching and AI self-heal.
selenium_find_elementseleniumFinds elements by XPath, text, id, name, CSS and link text.
selenium_page_sourceseleniumSearches the page HTML. Doesn't list interactive elements.
selenium_screenshotseleniumScreenshots for OCR and image matching.
playwright_find_elementplaywrightFinds elements by xpath=, css= and text= locators.
playwright_page_sourceplaywrightSearches the page and lists interactive elements.
playwright_screenshotplaywrightScreenshots for OCR and image matching.
camera_screenshotAnyFrames from a webcam or a TCP frame server. Used with ble.

optics doctor fails a project when no enabled element source starts with the enabled driver's name.

text_detection#

NameExtraSettings
easyocreasyocrNone. Accurate but slow on CPU. Downloads its models on first use. The language is always English.
pytesseractpytesseractNone. Needs the Tesseract program installed and on your PATH.
google_visiongoogle-visionNone. Credentials come from GOOGLE_APPLICATION_CREDENTIALS. googlevision also works.
remote_ocrNoneurl (required): your OCR service. capabilities: timeout (30), method (easyocr), language (en). Optics posts screenshots to <url>/detect-text.

image_detection#

NameExtraSettings
templatematchNonecapabilities.sift_nfeatures (2000). Finds template images, including scaled or rotated ones. Images are found by file name anywhere in the project.
remote_oirNoneurl (required): your image service. capabilities: timeout (30), method (template_matching). Optics posts to <url>/detect-image.

llm_models#

NameExtraSettings
geminillmAll optional: model (gemini-2.5-flash), temperature (0.0), use_vertexai, project, location.
llm_models:
  - gemini:
      enabled: true
      capabilities:
        model: gemini-2.5-flash

Gemini reads its credentials from the environment: GEMINI_API_KEY or GOOGLE_API_KEY, or GOOGLE_GENAI_USE_VERTEXAI=true with GOOGLE_CLOUD_PROJECT, GOOGLE_CLOUD_LOCATION and GOOGLE_APPLICATION_CREDENTIALS. Don't put an API key in config.yaml: Optics accepts api_key there but warns, because the file usually ends up in version control.

Examples by platform#

config.yaml
driver_sources:
  - appium:
      enabled: true
      url: "http://127.0.0.1:4723"        # required; the Appium server
      capabilities:                        # must not be empty
        platformName: Android              # required; picks the Android profile
        automationName: UiAutomator2
        deviceName: emulator-5554
        # udid: emulator-5554              # pin one device when several are connected
        appPackage: com.birch.shop         # the app Launch App opens
        appActivity: com.birch.shop.MainActivity
elements_sources:
  - appium_find_element: {enabled: true}   # XPath, accessibility id, class
  - appium_page_source:  {enabled: true}   # UI tree search (uses strict_element_match)
  - appium_screenshot:   {enabled: true}   # screenshots for OCR, images, self-heal
text_detection:
  - easyocr: {enabled: true}               # extra: easyocr
image_detection:
  - templatematch: {enabled: true}
log_level: INFO
file_log: true
json_log: true

Before optics execute, Optics checks that the Appium server answers and that adb devices lists a device.

Appium capabilities#

Optics reads only platformName and the app id; it passes every other capability to Appium unchanged. You can write any key with the appium: prefix, for example appium:udid.

platformNameApp id capabilityDefaults Optics adds (your values win)
AndroidappPackage and appActivitynoReset, newCommandTimeout: 3600, forceAppLaunch, shouldTerminateApp, ignoreHiddenApiPolicyError and a few others
iOSbundleIdThe same mobile defaults, without ignoreHiddenApiPolicyError
TizenTVappPackagenewCommandTimeout: 3600, rcMode: remote, noReset: true
LGTVappIdnewCommandTimeout: 600, rcMode: rc, useSecureWebsocket: true, autoExtendDevMode: false, noReset: true

Any other platformName fails with E0104. To attach to an Appium session that's already running, add appium:existingSessionId. Optics starts a new session if it can't attach.

TV#

Android TV uses the Android example. Samsung and LG TVs use Appium's TV drivers:

config.yaml (Samsung Tizen)
driver_sources:
  - appium:
      enabled: true
      url: "http://localhost:4723"
      capabilities:
        platformName: TizenTV
        appium:automationName: TizenTV
        appium:deviceAddress: 192.168.1.50
        appium:rcToken: "14184553"
        appium:appPackage: com.birch.tv
        appium:app: /path/to/birch.wgt
        appium:chromedriverExecutableDir: /path/to/chromedrivers
elements_sources:
  - appium_page_source: {enabled: true}
  - appium_screenshot:  {enabled: true}
config.yaml (LG webOS)
driver_sources:
  - appium:
      enabled: true
      url: "http://localhost:4723"
      capabilities:
        platformName: LGTV
        appium:automationName: webOS
        appium:deviceHost: 192.168.1.50
        appium:appId: com.birch.tv
elements_sources:
  - appium_screenshot: {enabled: true}

On Tizen and webOS, touch and typing keywords fail with E0105. Move around with Press Keycode and remote keys: UP, DOWN, LEFT, RIGHT, ENTER, BACK, HOME, PLAY, PAUSE, STOP, REWIND, FF. See Test TV apps.

BLE and camera#

The ble driver taps and types through a Bluetooth mouse-and-keyboard dongle. A camera or frame server provides the screen, and Optics finds elements with OCR or image templates.

config.yaml
driver_sources:
  - ble:
      enabled: true
      capabilities:
        device_id: "Birch test phone"     # required
        port: "/dev/ttyACM0"              # required: device path, COM port or USB product name
        pixel_width: 1080                 # the device screen in pixels
        pixel_height: 2336
        mickeys_width: 1080               # the device screen in mouse units
        mickeys_height: 2336
        x_invert: 1                       # 1 or -1
        y_invert: 1
elements_sources:
  - camera_screenshot:
      enabled: true
      capabilities:
        camera_index: 0                   # a webcam, or url: "192.168.1.20:9000" for a TCP frame server
text_detection:
  - easyocr: {enabled: true}
image_detection:
  - templatematch: {enabled: true}
ble capabilityDefaultWhat it does
device_idRequiredA name for the device.
portRequiredThe dongle's serial port (/dev/ttyACM0, COM3) or its USB product description.
pixel_width, pixel_height0The device screen size in pixels. Set them: taps can't be placed without them.
mickeys_width, mickeys_height0The device screen size in mouse movement units. Set them too.
x_invert, y_invert1-1 reverses the direction of mouse movement on that axis.
camera_screenshot capabilityDefaultWhat it does
camera_indexNoneThe webcam number. Use this or url.
urlNoneA TCP frame server as host:port.
header_size8Bytes in the frame server's length header: 4 or 8.
byte_orderlittleThe length header's byte order: little or big.

The BLE driver can't launch apps, so Launch App fails. Open the app by hand or with keys before the run. XPath and text locators don't work without a UI tree; use text that OCR can read, or image templates. See Test production devices.

include and exclude#

Run a subset of test cases. There are no CLI flags for this.

include:
  - Shopper can sign in
  - Shopper can add Trail Runner 2 to the cart
# or
exclude:
  - Shopper can check out
  • Names match the test_case column exactly, ignoring case.
  • Set one of the two. Setting both fails with E0403.
  • Test cases with "setup" or "teardown" in their names always run.

Logging#

KeyDefaultWhat it does
log_levelINFOThe level for the console and the log files. Set DEBUG to see every strategy Optics tries.
file_logfalseWrites internal_logs.log and execution_logs.log to the output folder. Each rotates at 10 MB and keeps 10 old files.
log_pathNoneSends both logs to this one file.
json_logfalseWrites logs.json: a list of events with name, status, timings and log lines for each test case, module and keyword.
json_pathNoneWrites the JSON events here instead.
consoletrueNo effect in 1.10.5.

With file_log: true and no execution_output_path, Optics also writes a copy of both logs to logs/ in the folder you ran optics from.

Values you write with the @: prefix, such as @:s3cret, are masked as **** in log files. See Read results.

ai_self_heal#

When an element can't be found, self-heal sends a screenshot to an LLM and lets it try up to five corrective steps, such as scrolling or closing a popup.

ai_self_heal: true
llm_models:
  - gemini:
      enabled: true
  • It needs an enabled llm_models entry, the llm extra, Gemini credentials, and a screenshot element source such as appium_screenshot. Without them it does nothing, and the run isn't affected.
  • It applies to keywords that find and act on an element: Press Element, Enter Text, Enter Number, Clear Element Text, Get Text, Swipe From Element, Scroll From Element, Detect And Press and Select Dropdown Option. It doesn't apply to assertions.
  • A healed step passes. Check the logs for AI self-heal: lines so a real regression doesn't go unnoticed.

optics serve and optics mcp ignore this key. They use per-session fields and the OPTICS_AI_SELF_HEAL variable instead. See Self-heal.

strict_element_match#

strict_element_match: true

By default the page-source element sources (appium_page_source, selenium_page_source, playwright_page_source) accept close or partial text matches, so Sign in can match Sign in with email. Set strict_element_match: true to accept only exact matches. Turn it on when similar labels make tests press the wrong element.

save_captures#

save_captures: false

When false, Capture Screenshot, Capture Pagesource and Get Screen Elements stop writing files. Optics still saves the screenshot it takes before each action, the annotated screenshots and the page source logs. optics serve always turns captures off.

Starter config#

optics init writes a commented config.yaml with every engine disabled. To get started, enable one driver, enable its three element sources, and install the driver's extra with optics setup --install <name>. See Install Optics.